From f5001c8464dcd6e18dcf8455211b1e1e77fbce95 Mon Sep 17 00:00:00 2001 From: Element CI Date: Tue, 24 Sep 2024 07:23:55 -0700 Subject: [PATCH 001/114] Prepare next release --- CHANGES.md | 26 ++++++++++++++++++++++++++ ElementX.xcodeproj/project.pbxproj | 4 ++-- project.yml | 2 +- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8ae2d2c28c..a126a9f5aa 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,29 @@ +## Changes in 1.8.4 (2024-09-24) + +### What's Changed + +✨ Features +* Enable message pinning by @Velin92 in https://github.com/element-hq/element-x-ios/pull/3308 + +🐛 Bugfixes +* Fix: confusion of lab flags for invisible crypto by @BillCarsonFr in https://github.com/element-hq/element-x-ios/pull/3319 +* Fix a regression where you can't scroll the timeline on iOS 17 by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3320 +* Fix a bug where the Join Room screen was sometimes shown instead of the Room. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3323 +* Fix a bug on iOS 18 where the timeline background would use the wrong colour scheme when using the app switcher. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3324 +* Don't use the new iPad modal presentation mode for the timeline item menu by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3325 + +🗣 Translations +* Translations update by @RiotRobot in https://github.com/element-hq/element-x-ios/pull/3315 + +🧱 Build +* Update the project to use Xcode 16. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3303 + +Others +* A bunch of random tweaks. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3317 + + +**Full Changelog**: https://github.com/element-hq/element-x-ios/compare/1.8.3...1.8.4 + ## Changes in 1.8.3 (2024-09-19) ### What's Changed diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 8d5496b220..9a67d8e036 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7406,7 +7406,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.8.4; + MARKETING_VERSION = 1.8.5; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCTION_APP_NAME = Element; @@ -7483,7 +7483,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.8.4; + MARKETING_VERSION = 1.8.5; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; diff --git a/project.yml b/project.yml index 04567db8db..556fe5787e 100644 --- a/project.yml +++ b/project.yml @@ -41,7 +41,7 @@ settings: APP_GROUP_IDENTIFIER: group.$(BASE_APP_GROUP_IDENTIFIER) APP_NAME: ElementX KEYCHAIN_ACCESS_GROUP_IDENTIFIER: "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)" - MARKETING_VERSION: 1.8.4 + MARKETING_VERSION: 1.8.5 CURRENT_PROJECT_VERSION: 1 SUPPORTS_MACCATALYST: false From 6faa26eadedf6897601119c4076d1e46f0eff9d5 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Wed, 25 Sep 2024 10:01:21 +0100 Subject: [PATCH 002/114] Add detection for latest devices. (#3327) * Update various packages. Notable changes: - DeviceKit now detects iPad Pro (M4), iPad Air (M2) and iPhones 16. - KZFileWatchers isn't updated but now has a tag to pin to. * Try using a raw destination for tests. To fix an issue where CI isn't finding iPhone 16 when it's clearly listed by simctl. --- ElementX.xcodeproj/project.pbxproj | 10 +++++----- .../xcshareddata/swiftpm/Package.resolved | 16 ++++++++-------- fastlane/Fastfile | 6 +++--- project.yml | 8 ++++---- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 9a67d8e036..b29471d756 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7726,8 +7726,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/krzysztofzablocki/KZFileWatchers"; requirement = { - branch = master; - kind = branch; + kind = upToNextMinorVersion; + minimumVersion = 1.2.0; }; }; 44FA555384AD79668D886043 /* XCRemoteSwiftPackageReference "matrix-rich-text-editor-swift" */ = { @@ -7783,7 +7783,7 @@ repositoryURL = "https://github.com/nicklockwood/GZIP"; requirement = { kind = upToNextMinorVersion; - minimumVersion = 1.3.0; + minimumVersion = 1.3.2; }; }; 821C67C9A7F8CC3FD41B28B4 /* XCRemoteSwiftPackageReference "emojibase-bindings" */ = { @@ -7847,7 +7847,7 @@ repositoryURL = "https://github.com/devicekit/DeviceKit"; requirement = { kind = upToNextMinorVersion; - minimumVersion = 5.2.2; + minimumVersion = 5.5.0; }; }; E025F19D013D9BA6C58B37F4 /* XCRemoteSwiftPackageReference "swift-algorithms" */ = { @@ -7879,7 +7879,7 @@ repositoryURL = "https://github.com/mxcl/Version"; requirement = { kind = upToNextMinorVersion; - minimumVersion = 2.0.0; + minimumVersion = 2.1.0; }; }; F71C70A4404CC6D9C4AF35F2 /* XCRemoteSwiftPackageReference "compound-ios" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index aedc0764c2..7afb65f791 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/devicekit/DeviceKit", "state" : { - "revision" : "fe41d18eccd92a115cffaa35dfff03018c67e635", - "version" : "5.2.2" + "revision" : "7ff5331960151aec74fb422e1d45f54ef6cc086d", + "version" : "5.5.0" } }, { @@ -68,8 +68,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/nicklockwood/GZIP", "state" : { - "revision" : "bd55f1d89e71ae3f481da74cd4adeadbb849620c", - "version" : "1.3.1" + "revision" : "f710a37aa978a93b815a4f64bd504dc4c3256312", + "version" : "1.3.2" } }, { @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/krzysztofzablocki/KZFileWatchers", "state" : { - "branch" : "master", - "revision" : "d27a9557427d261adccdf4b566acc9d9c0fec6f4" + "revision" : "d27a9557427d261adccdf4b566acc9d9c0fec6f4", + "version" : "1.2.0" } }, { @@ -284,8 +284,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/mxcl/Version", "state" : { - "revision" : "1fe824b80d89201652e7eca7c9252269a1d85e25", - "version" : "2.0.1" + "revision" : "303a0f916772545e1e8667d3104f83be708a723c", + "version" : "2.1.0" } } ], diff --git a/fastlane/Fastfile b/fastlane/Fastfile index d6b573b818..05ea80f629 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -82,7 +82,7 @@ end lane :unit_tests do |options| run_tests( scheme: "UnitTests", - device: 'iPhone 16', + destination: "platform=iOS Simulator,name=iPhone 16,OS=18.0", ensure_devices_found: true, result_bundle: true, number_of_retries: 3, @@ -91,7 +91,7 @@ lane :unit_tests do |options| if !options[:skip_previews] run_tests( scheme: "PreviewTests", - device: 'iPhone SE (3rd generation)', + destination: "platform=iOS Simulator,name=iPhone SE (3rd generation),OS=18.0", ensure_devices_found: true, result_bundle: true, number_of_retries: 3, @@ -147,7 +147,7 @@ lane :integration_tests do run_tests( scheme: "IntegrationTests", - devices: ["iPhone 16 Pro"], + destination: "platform=iOS Simulator,name=iPhone 16 Pro,OS=18.0", ensure_devices_found: true, result_bundle: true, reset_simulator: true diff --git a/project.yml b/project.yml index 556fe5787e..45505258a4 100644 --- a/project.yml +++ b/project.yml @@ -91,7 +91,7 @@ packages: minorVersion: 1.0.0 DeviceKit: url: https://github.com/devicekit/DeviceKit - minorVersion: 5.2.2 + minorVersion: 5.5.0 DSWaveformImage: url: https://github.com/dmrschmidt/DSWaveformImage exactVersion: 14.1.1 @@ -100,7 +100,7 @@ packages: exactVersion: 1.6.26 GZIP: url: https://github.com/nicklockwood/GZIP - minorVersion: 1.3.0 + minorVersion: 1.3.2 KeychainAccess: url: https://github.com/kishikawakatsumi/KeychainAccess minorVersion: 4.2.0 @@ -109,7 +109,7 @@ packages: minorVersion: 7.6.0 KZFileWatchers: url: https://github.com/krzysztofzablocki/KZFileWatchers - branch: master + minorVersion: 1.2.0 LoremSwiftum: url: https://github.com/lukaskubanek/LoremSwiftum minorVersion: 2.2.1 @@ -133,7 +133,7 @@ packages: minorVersion: 6.0.0 Version: url: https://github.com/mxcl/Version - minorVersion: 2.0.0 + minorVersion: 2.1.0 aggregateTargets: Periphery: From 864bd23d350c905eb5f4b2607946e259c381046a Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Wed, 25 Sep 2024 11:00:18 +0100 Subject: [PATCH 003/114] Use a plain view for reactions instead of a TabView. (#3328) We don't use any of the TabView features and it shows a tab bar at the bottom when the list is long. --- .../Supplementary/ReactionsSummaryView.swift | 21 ++++++++++--------- ...test_reactionsSummaryView-iPad-en-GB.1.png | 4 ++-- ...est_reactionsSummaryView-iPad-pseudo.1.png | 4 ++-- ...reactionsSummaryView-iPhone-16-en-GB.1.png | 4 ++-- ...eactionsSummaryView-iPhone-16-pseudo.1.png | 4 ++-- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/ElementX/Sources/Screens/Timeline/View/Supplementary/ReactionsSummaryView.swift b/ElementX/Sources/Screens/Timeline/View/Supplementary/ReactionsSummaryView.swift index 5f535f7063..130d612eb9 100644 --- a/ElementX/Sources/Screens/Timeline/View/Supplementary/ReactionsSummaryView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Supplementary/ReactionsSummaryView.swift @@ -13,6 +13,9 @@ struct ReactionsSummaryView: View { let mediaProvider: MediaProviderProtocol? @State var selectedReactionKey: String + private var selectedReaction: AggregatedReaction? { + reactions.first { $0.key == selectedReactionKey } + } var body: some View { VStack(alignment: .leading, spacing: 0) { @@ -49,19 +52,17 @@ struct ReactionsSummaryView: View { .padding(.bottom, 12) } + @ViewBuilder private var sendersList: some View { - TabView(selection: $selectedReactionKey) { - ForEach(reactions) { reaction in - ScrollView { - VStack(alignment: .leading, spacing: 8) { - ForEach(reaction.senders) { sender in - ReactionSummarySenderView(sender: sender, member: members[sender.id], mediaProvider: mediaProvider) - .padding(.horizontal, 16) - } + if let selectedReaction { + ScrollView { + VStack(alignment: .leading, spacing: 8) { + ForEach(selectedReaction.senders) { sender in + ReactionSummarySenderView(sender: sender, member: members[sender.id], mediaProvider: mediaProvider) + .padding(.horizontal, 16) } - .frame(maxWidth: .infinity) } - .tag(reaction.key) + .frame(maxWidth: .infinity) } } } diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_reactionsSummaryView-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_reactionsSummaryView-iPad-en-GB.1.png index 4973c50d8f..dd22b7ebf2 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_reactionsSummaryView-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_reactionsSummaryView-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b1421a726565df2dad3ab8f0febd352f23489ac4ca583905740ffffeb5931660 -size 157682 +oid sha256:0a525081a32f80cdb362055bf1fb85bb94e36ee4f8eda0fed3348a66c31863bd +size 156020 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_reactionsSummaryView-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_reactionsSummaryView-iPad-pseudo.1.png index 1d0d0f5784..8535a23c1c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_reactionsSummaryView-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_reactionsSummaryView-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:28768fa226f7387ef2e1c257364af742d0b201c3b1e2200d5bf9c5b6cd7a5ede -size 158659 +oid sha256:17732eaf6452abe88755241d6c08304f1732d0bfd5545d580112ab9f23531071 +size 156140 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_reactionsSummaryView-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_reactionsSummaryView-iPhone-16-en-GB.1.png index d24f5b46fb..aea3955145 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_reactionsSummaryView-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_reactionsSummaryView-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e4ee2d0a1b36a89d157d405a42124c399b48b6d63ecad48c8e529532e7a61fa -size 65270 +oid sha256:190f8ea7d63ff14e77714718feafc1e6d777f5eb2abcaa65849ec1c78d763776 +size 64131 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_reactionsSummaryView-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_reactionsSummaryView-iPhone-16-pseudo.1.png index 4ea8d217d5..becb824371 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_reactionsSummaryView-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_reactionsSummaryView-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a6a9a8bd8535ba80b94570c01e299999dd63d268e55b30e76edade00316d606 -size 65983 +oid sha256:ccf83508eff98903ae25de15107818345cef052868b1b5854ab4379e470d0c68 +size 64217 From af8c16150b6ad83c3fdda77e2fa5f1326879b4eb Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Wed, 25 Sep 2024 13:10:02 +0100 Subject: [PATCH 004/114] Upgrade Kingfisher to fix a bug that prevented GIFs from being tapped. (#3326) * Upgrade Kingfisher to fix a bug the prevented GIFs from being tapped. https://github.com/onevcat/Kingfisher/issues/2295 * Fix tests. --- ElementX.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 ++-- .../Services/Media/Provider/MediaProvider.swift | 16 +++------------- .../Sources/MediaProvider/MockImageCache.swift | 3 ++- project.yml | 2 +- 5 files changed, 9 insertions(+), 18 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index b29471d756..d502d93804 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7839,7 +7839,7 @@ repositoryURL = "https://github.com/onevcat/Kingfisher"; requirement = { kind = upToNextMinorVersion; - minimumVersion = 7.6.0; + minimumVersion = 8.0.3; }; }; D5F7D47BBAAE0CF1DDEB3034 /* XCRemoteSwiftPackageReference "DeviceKit" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7afb65f791..dfbedefd5d 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -86,8 +86,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/onevcat/Kingfisher", "state" : { - "revision" : "af4be924ad984cf4d16f4ae4df424e79a443d435", - "version" : "7.6.2" + "revision" : "33696a71dd4d71f1b0f781ade11a48ba5549581a", + "version" : "8.0.3" } }, { diff --git a/ElementX/Sources/Services/Media/Provider/MediaProvider.swift b/ElementX/Sources/Services/Media/Provider/MediaProvider.swift index b87f42603e..408e858748 100644 --- a/ElementX/Sources/Services/Media/Provider/MediaProvider.swift +++ b/ElementX/Sources/Services/Media/Provider/MediaProvider.swift @@ -38,8 +38,8 @@ struct MediaProvider: MediaProviderProtocol { } let cacheKey = cacheKeyForURL(source.url, size: size) - - if case let .success(cacheResult) = await imageCache.retrieveImage(forKey: cacheKey), + + if let cacheResult = try? await imageCache.retrieveImage(forKey: cacheKey, options: nil), let image = cacheResult.image { return .success(image) } @@ -57,7 +57,7 @@ struct MediaProvider: MediaProviderProtocol { return .failure(.invalidImageData) } - imageCache.store(image, forKey: cacheKey) + try await imageCache.store(image, forKey: cacheKey) return .success(image) } catch { @@ -149,13 +149,3 @@ struct MediaProvider: MediaProviderProtocol { } } } - -private extension ImageCache { - func retrieveImage(forKey key: String) async -> Result { - await withCheckedContinuation { continuation in - retrieveImage(forKey: key) { result in - continuation.resume(returning: result) - } - } - } -} diff --git a/UnitTests/Sources/MediaProvider/MockImageCache.swift b/UnitTests/Sources/MediaProvider/MockImageCache.swift index 4e49317e65..64c41bde5e 100644 --- a/UnitTests/Sources/MediaProvider/MockImageCache.swift +++ b/UnitTests/Sources/MediaProvider/MockImageCache.swift @@ -5,7 +5,7 @@ // Please see LICENSE in the repository root for full details. // @testable import ElementX -import Kingfisher +@testable import Kingfisher import UIKit class MockImageCache: ImageCache { @@ -35,5 +35,6 @@ class MockImageCache: ImageCache { callbackQueue: CallbackQueue = .untouch, completionHandler: ((CacheStoreResult) -> Void)? = nil) { storedImages[key] = image + completionHandler?(.init(memoryCacheResult: .success(()), diskCacheResult: .success(()))) } } diff --git a/project.yml b/project.yml index 45505258a4..281902718b 100644 --- a/project.yml +++ b/project.yml @@ -106,7 +106,7 @@ packages: minorVersion: 4.2.0 Kingfisher: url: https://github.com/onevcat/Kingfisher - minorVersion: 7.6.0 + minorVersion: 8.0.3 KZFileWatchers: url: https://github.com/krzysztofzablocki/KZFileWatchers minorVersion: 1.2.0 From a8dbda90d955b5704112d275de40fac645c17d99 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Wed, 25 Sep 2024 14:40:18 +0100 Subject: [PATCH 005/114] Configure the AuthenticationService later now that we have 2 flows on the start screen. (#3316) * Don't query the homeserver until confirming it (or selecting a different one). * Setup the infrastructure to test AuthenticationService. Implement basic tests for configuration & password login. * Use the real AuthenticationService with a mock Client in all of the tests. * Add tests for the ServerConfirmationScreenViewModel. * Remove redundant view state and test for it. --- ElementX.xcodeproj/project.pbxproj | 48 +- .../Sources/Application/AppCoordinator.swift | 2 +- .../AuthenticationFlowCoordinator.swift | 65 +-- ...thenticationClientBuilderFactoryMock.swift | 22 + .../AuthenticationClientBuilderMock.swift | 47 ++ .../Mocks/Generated/GeneratedMocks.swift | 489 ++++++++++++++++++ .../Sources/Mocks/SDK/ClientSDKMock.swift | 75 +++ .../{ => SDK}/EventTimelineItemSDKMock.swift | 0 .../Sources/Mocks/UserSessionStoreMock.swift | 17 + .../LoginScreen/LoginHomeserver.swift | 5 + .../LoginScreen/LoginScreenCoordinator.swift | 2 +- .../ServerConfirmationScreenCoordinator.swift | 6 +- .../ServerConfirmationScreenModels.swift | 36 +- .../ServerConfirmationScreenViewModel.swift | 98 +++- .../View/ServerConfirmationScreen.swift | 15 +- .../MockServerSelectionScreenState.swift | 14 +- .../ServerSelectionScreenCoordinator.swift | 11 +- .../ServerSelectionScreenModels.swift | 12 +- .../ServerSelectionScreenViewModel.swift | 11 +- .../View/ServerSelectionScreen.swift | 11 +- .../AuthenticationClientBuilder.swift | 14 +- .../AuthenticationClientBuilderFactory.swift | 33 ++ .../AuthenticationService.swift | 53 +- .../AuthenticationServiceProtocol.swift | 10 +- .../MockAuthenticationService.swift | 65 --- .../Services/QRCode/QRCodeLoginService.swift | 2 +- .../UserSession/UserSessionStore.swift | 4 +- .../UserSessionStoreProtocol.swift | 3 +- .../UITests/UITestsAppCoordinator.swift | 16 +- .../UITests/UITestsScreenIdentifier.swift | 1 - ...AuthenticationFlowCoordinatorUITests.swift | 37 +- UITests/Sources/LoginScreenUITests.swift | 35 -- ...onFlow-1-iPad-10th-generation-en-GB.UI.png | 3 + ...uthenticationFlow-1-iPhone-15-en-GB.UI.png | 3 + .../login-0-iPad-10th-generation-en-GB.UI.png | 3 - .../login-0-iPhone-16-en-GB.UI.png | 3 - .../login-1-iPad-10th-generation-en-GB.UI.png | 3 - .../login-1-iPhone-16-en-GB.UI.png | 3 - .../login-iPad-10th-generation-en-GB.UI.png | 3 - .../Application/login-iPhone-16-en-GB.UI.png | 3 - .../Sources/AuthenticationServiceTests.swift | 96 ++++ ...verConfigurationScreenViewStateTests.swift | 11 +- ...rverConfirmationScreenViewModelTests.swift | 114 +++- .../ServerSelectionViewModelTests.swift | 3 +- 44 files changed, 1209 insertions(+), 298 deletions(-) create mode 100644 ElementX/Sources/Mocks/AuthenticationClientBuilderFactoryMock.swift create mode 100644 ElementX/Sources/Mocks/AuthenticationClientBuilderMock.swift create mode 100644 ElementX/Sources/Mocks/SDK/ClientSDKMock.swift rename ElementX/Sources/Mocks/{ => SDK}/EventTimelineItemSDKMock.swift (100%) create mode 100644 ElementX/Sources/Mocks/UserSessionStoreMock.swift create mode 100644 ElementX/Sources/Services/Authentication/AuthenticationClientBuilderFactory.swift delete mode 100644 ElementX/Sources/Services/Authentication/MockAuthenticationService.swift delete mode 100644 UITests/Sources/LoginScreenUITests.swift create mode 100644 UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPad-10th-generation-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPhone-15-en-GB.UI.png delete mode 100644 UITests/Sources/__Snapshots__/Application/login-0-iPad-10th-generation-en-GB.UI.png delete mode 100644 UITests/Sources/__Snapshots__/Application/login-0-iPhone-16-en-GB.UI.png delete mode 100644 UITests/Sources/__Snapshots__/Application/login-1-iPad-10th-generation-en-GB.UI.png delete mode 100644 UITests/Sources/__Snapshots__/Application/login-1-iPhone-16-en-GB.UI.png delete mode 100644 UITests/Sources/__Snapshots__/Application/login-iPad-10th-generation-en-GB.UI.png delete mode 100644 UITests/Sources/__Snapshots__/Application/login-iPhone-16-en-GB.UI.png create mode 100644 UnitTests/Sources/AuthenticationServiceTests.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index d502d93804..8aeb88c97b 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -83,6 +83,7 @@ 0EA6537A07E2DC882AEA5962 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 187853A7E643995EE49FAD43 /* Localizable.stringsdict */; }; 0EE5EBA18BA1FE10254BB489 /* UIFont+AttributedStringBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = E8CA187FE656EE5A3F6C7DE5 /* UIFont+AttributedStringBuilder.m */; }; 0EEC614342F823E5BF966C2C /* AppLockTimerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5B4CD611DE7E94F5BA87B2 /* AppLockTimerTests.swift */; }; + 0F4709282FCCFBEFED427B8A /* AuthenticationClientBuilderMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4760CE2128FBC217304272AB /* AuthenticationClientBuilderMock.swift */; }; 0F6C8033FA60CFD36F7CA205 /* AppLockSetupPINScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A019A12C866D64CF072024B9 /* AppLockSetupPINScreenViewModel.swift */; }; 108D3C0707A90B0F848CDBB9 /* ResolveVerifiedUserSendFailureScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60011EF0086E49DBD78E16E5 /* ResolveVerifiedUserSendFailureScreenModels.swift */; }; 109AEB7D33C4497727AFB87F /* TimelineInteractionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BA894BC09972DC45E497D37 /* TimelineInteractionHandler.swift */; }; @@ -150,6 +151,7 @@ 206F0DBAB6AF042CA1FF2C0D /* SettingsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D487C1185D658F8B15B8F55 /* SettingsViewModelTests.swift */; }; 208C19811613F9A10F8A7B75 /* MediaLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AFCE895ECFFA53FEE64D62B /* MediaLoader.swift */; }; 20C16A3F718802B0E4A19C83 /* URLComponentsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76310030C831D4610A705603 /* URLComponentsTests.swift */; }; + 210DB40676DF2A23E69C2D06 /* AuthenticationClientBuilderFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = B655A536341D2695158C6664 /* AuthenticationClientBuilderFactory.swift */; }; 2118E35D312951B241067BD5 /* MessageComposerTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345172AD4377E83A44BD864F /* MessageComposerTextField.swift */; }; 211B5F524E851178EE549417 /* CurrentValuePublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 127C8472672A5BA09EF1ACF8 /* CurrentValuePublisher.swift */; }; 21813AF91CFC6F3E3896DB53 /* AppLockSetupBiometricsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10F130DF775CE6BC51A4E392 /* AppLockSetupBiometricsScreenModels.swift */; }; @@ -225,7 +227,6 @@ 3116693C5EB476E028990416 /* XCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74611A4182DCF5F4D42696EC /* XCTestCase.swift */; }; 3118D9ABFD4BE5A3492FF88A /* ElementCallConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC437C491EA6996513B1CEAB /* ElementCallConfiguration.swift */; }; 32B7891D937377A59606EDFC /* UserFlowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21DD8599815136EFF5B73F38 /* UserFlowTests.swift */; }; - 32FC143630CE22A9E403370B /* MockAuthenticationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA38899517F08FE2AF34EB45 /* MockAuthenticationService.swift */; }; 339BC18777912E1989F2F17D /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = 584A61D9C459FAFEF038A7C0 /* Section.swift */; }; 33CAC1226DFB8B5D8447D286 /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 1BCD21310B997A6837B854D6 /* GZIP */; }; 33F1FB19F222BA9930AB1A00 /* RoomListFiltersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6372DD10DED30E7AD7BCE21 /* RoomListFiltersView.swift */; }; @@ -290,6 +291,7 @@ 407DCE030E0F9B7C9861D38A /* LRUCache in Frameworks */ = {isa = PBXBuildFile; productRef = 1081D3630AAD3ACEDDEC3A98 /* LRUCache */; }; 40B79D20A873620F7F128A2C /* UserPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FA991289149D31F4286747 /* UserPreference.swift */; }; 414F50CFCFEEE2611127DCFB /* RestorationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3558A15CFB934F9229301527 /* RestorationToken.swift */; }; + 41C5DA0C06F30311A221E85B /* ClientSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EAF4A49F3ACD8BB8B0D2371 /* ClientSDKMock.swift */; }; 41CE5E1289C8768FC5B6490C /* RoomTimelineItemViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2D52E36AD614B3C003EF6 /* RoomTimelineItemViewState.swift */; }; 41DFDD212D1BE57CA50D783B /* KZFileWatchers in Frameworks */ = {isa = PBXBuildFile; productRef = 81DB3AB6CE996AB3954F4F03 /* KZFileWatchers */; }; 41F553349AF44567184822D8 /* APNSPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94D670124FC3E84F23A62CCF /* APNSPayload.swift */; }; @@ -405,10 +407,10 @@ 5AE6404C4FD4848ACCFF9EDC /* SecureBackupLogoutConfirmationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1573D28C8A9FB6399D0EEFB /* SecureBackupLogoutConfirmationScreenCoordinator.swift */; }; 5B6E5AD224509E6C0B520D6E /* RoomMemberDetailsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DDF49CEBC0DFC59C308335F /* RoomMemberDetailsScreenViewModelProtocol.swift */; }; 5B7D24A318AFF75AD611A026 /* RoomDirectorySearchScreenScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6BFF453838CF6C3982C5A3 /* RoomDirectorySearchScreenScreenViewModelTests.swift */; }; + 5BBDF9926CB645DE2F7BC258 /* EventTimelineItemSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86D447D771CEF6194348F5F /* EventTimelineItemSDKMock.swift */; }; 5BC6C4ADFE7F2A795ECDE130 /* SecureBackupKeyBackupScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2D4EEBE8C098BBADD10939 /* SecureBackupKeyBackupScreenCoordinator.swift */; }; 5C02841B2A86327B2C377682 /* NotificationConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C830A64609CBD152F06E0457 /* NotificationConstants.swift */; }; 5C164551F7D26E24F09083D3 /* StaticLocationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C616D90B1E2F033CAA325439 /* StaticLocationScreenViewModelProtocol.swift */; }; - 5C8AFBF168A41E20835F3B86 /* LoginScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DB34B0C74CD242FED9DD069 /* LoginScreenUITests.swift */; }; 5D27B6537591471A42C89027 /* EmoteRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450E04B2A976CC4C8CC1807C /* EmoteRoomTimelineItem.swift */; }; 5D52925FEB1B780C65B0529F /* PinnedEventsTimelineScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA4F6D7000EDCD187E0989E7 /* PinnedEventsTimelineScreen.swift */; }; 5D53AE9342A4C06B704247ED /* MediaLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A02406480C351B8C6E0682C /* MediaLoaderProtocol.swift */; }; @@ -541,6 +543,7 @@ 79741C1953269FF1A211D246 /* RoomPollsHistoryScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E14FF533D25A0692F7CEB0 /* RoomPollsHistoryScreenViewModel.swift */; }; 798BF3072137833FBD3F4C96 /* TimelineDeliveryStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F91544AC136BF6477BDAB8 /* TimelineDeliveryStatusView.swift */; }; 79959F8E45C3749997482A7F /* TimelineItemBubbledStylerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A459AE4B6566B2FA99E86B2 /* TimelineItemBubbledStylerView.swift */; }; + 79D57E9AE03A2DC689D14EA2 /* UserSessionStoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB9BA2F30EB8C33226D8FF1 /* UserSessionStoreMock.swift */; }; 7A02EB29F3B993AB20E0A198 /* RoomPollsHistoryScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42C8C368A611B9CB79C7F5FA /* RoomPollsHistoryScreen.swift */; }; 7A0D335D38ECA095A575B4F7 /* TimelineStyler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DB0E533508094156D8024C3 /* TimelineStyler.swift */; }; 7A170A5A4A352954BB2A1B96 /* AuthenticationStartScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E8C8817F59BEC7E358EB78 /* AuthenticationStartScreen.swift */; }; @@ -655,6 +658,7 @@ 915B4CDAF220D9AEB4047D45 /* PollInteractionHandlerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 259E5B05BDE6E20C26CF11B4 /* PollInteractionHandlerProtocol.swift */; }; 91ABC91758A6E4A5FAA2E9C4 /* ReadReceipt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 314F1C79850BE46E8ABEAFCB /* ReadReceipt.swift */; }; 91C6AC0E9D2B9C0C76CC6AD4 /* RoomDirectorySearchScreenScreenModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3984C93B8E9B10C92DADF9EE /* RoomDirectorySearchScreenScreenModelProtocol.swift */; }; + 92012C96039BC8C2CAEBA9E2 /* AuthenticationServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671C338B7259DC5774816885 /* AuthenticationServiceTests.swift */; }; 9219640F4D980CFC5FE855AD /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 536E72DCBEEC4A1FE66CFDCE /* target.yml */; }; 92720AB0DA9AB5EEF1DAF56B /* SecureBackupLogoutConfirmationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC017C3CB6B0F7C63F460F2 /* SecureBackupLogoutConfirmationScreenViewModel.swift */; }; 9278EC51D24E57445B290521 /* AudioSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB284643AF7AB131E307DCE0 /* AudioSessionProtocol.swift */; }; @@ -749,6 +753,7 @@ A4B123C635F70DDD4BC2FAC9 /* BlockedUsersScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E76A706B3EEA32B882DA5E2D /* BlockedUsersScreenViewModelProtocol.swift */; }; A4C29D373986AFE4559696D5 /* SecureBackupKeyBackupScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4525E8C0FBDD27D1ACE90952 /* SecureBackupKeyBackupScreenViewModelProtocol.swift */; }; 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 */; }; A5B9EF45C7B8ACEB4954AE36 /* LoginScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9780389F8A53E4D26E23DD03 /* LoginScreenViewModelProtocol.swift */; }; A5D551E5691749066E0E0C44 /* RoomDetailsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 837B440C4705E4B899BCB899 /* RoomDetailsScreenViewModel.swift */; }; @@ -1054,7 +1059,6 @@ EDF8919F15DE0FF00EF99E70 /* DocumentPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F5567A7EF6F2AB9473236F6 /* DocumentPicker.swift */; }; EE4E2C1922BBF5169E213555 /* PillAttachmentViewProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B53D6C5C0D14B04D3AB3F6E /* PillAttachmentViewProvider.swift */; }; EE56238683BC3ECA9BA00684 /* GlobalSearchScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA4D639E27D5882A6A71AECF /* GlobalSearchScreenViewModelTests.swift */; }; - EE57A96130DD8DB053790AE2 /* EventTimelineItemSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7A6BBC686B1F840FA807FB /* EventTimelineItemSDKMock.swift */; }; EE8491AD81F47DF3C192497B /* DecorationTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184CF8C196BE143AE226628D /* DecorationTimelineItemProtocol.swift */; }; EEAE954289DE813A61656AE0 /* LayoutDirection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14D83B2B7CD5501A0089EFC /* LayoutDirection.swift */; }; EEB9C1555C63B93CA9C372C2 /* EmojiPickerScreenHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B5E29E9A22F45534FBD5B58 /* EmojiPickerScreenHeaderView.swift */; }; @@ -1219,6 +1223,7 @@ 052B2F924572AFD70B5F500E /* StartChatScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreenViewModel.swift; sourceTree = ""; }; 054F469E433864CC6FE6EE8E /* ServerSelectionUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionUITests.swift; sourceTree = ""; }; 05512FB13987D221B7205DE0 /* HomeScreenRecoveryKeyConfirmationBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRecoveryKeyConfirmationBanner.swift; sourceTree = ""; }; + 0554FEA301486A8CFA475D5A /* AuthenticationClientBuilderFactoryMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationClientBuilderFactoryMock.swift; sourceTree = ""; }; 05596E4A11A8C9346E9E54AE /* SoftLogoutScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutScreenCoordinator.swift; sourceTree = ""; }; 05A3E8741D199CD1A37F4CBF /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; 05AF58372CA884A789EB9C5A /* AppMediatorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediatorProtocol.swift; sourceTree = ""; }; @@ -1311,7 +1316,6 @@ 1BA5A62DA4B543827FF82354 /* LAContextMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LAContextMock.swift; sourceTree = ""; }; 1C21A715237F2B6D6E80998C /* SecureBackupControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupControllerProtocol.swift; sourceTree = ""; }; 1C25B6EBEB414431187D73B7 /* TimelineReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReplyView.swift; sourceTree = ""; }; - 1C7A6BBC686B1F840FA807FB /* EventTimelineItemSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTimelineItemSDKMock.swift; sourceTree = ""; }; 1C7F63EB1525E697CAEB002B /* BlankFormCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlankFormCoordinator.swift; sourceTree = ""; }; 1CC575D1895FA62591451A93 /* RoomMemberDetailsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsScreen.swift; sourceTree = ""; }; 1CD7C0A2750998C2D77AD00F /* JoinRoomScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenViewModel.swift; sourceTree = ""; }; @@ -1323,7 +1327,6 @@ 1D9F148717D74F73BE724434 /* LongPressWithFeedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongPressWithFeedback.swift; sourceTree = ""; }; 1DA7E93C2E148B96EF6A8500 /* TimelineItemAccessibilityModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemAccessibilityModifier.swift; sourceTree = ""; }; 1DB2FC2AA9A07EE792DF65CF /* NotificationPermissionsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPermissionsScreenModels.swift; sourceTree = ""; }; - 1DB34B0C74CD242FED9DD069 /* LoginScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenUITests.swift; sourceTree = ""; }; 1DE7969EBCAF078813E18EA1 /* RoomRolesAndPermissionsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomRolesAndPermissionsScreenModels.swift; sourceTree = ""; }; 1DF8F7A3AD83D04C08D75E01 /* RoomDetailsEditScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreenViewModelProtocol.swift; sourceTree = ""; }; 1DFE0E493FB55E5A62E7852A /* ProposedViewSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProposedViewSize.swift; sourceTree = ""; }; @@ -1519,6 +1522,7 @@ 471BB7276C97AF60B3A5463B /* RoomDirectorySearchProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchProxy.swift; sourceTree = ""; }; 475D47D0BFE961B02BAC5D49 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = id; path = id.lproj/Localizable.stringsdict; sourceTree = ""; }; 475EB595D7527E9A8A14043E /* uz */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uz; path = uz.lproj/Localizable.strings; sourceTree = ""; }; + 4760CE2128FBC217304272AB /* AuthenticationClientBuilderMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationClientBuilderMock.swift; sourceTree = ""; }; 47873756E45B46683D97DC32 /* LegalInformationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreenModels.swift; sourceTree = ""; }; 47EBB5D698CE9A25BB553A2D /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = ""; }; 47F29139BC2A804CE5E0757E /* MediaUploadPreviewScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModel.swift; sourceTree = ""; }; @@ -1646,6 +1650,7 @@ 66AFD800AF033D8B0D11191A /* UserPropertiesExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPropertiesExt.swift; sourceTree = ""; }; 66F2402D738694F98729A441 /* RoomTimelineProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProvider.swift; sourceTree = ""; }; 66F91544AC136BF6477BDAB8 /* TimelineDeliveryStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineDeliveryStatusView.swift; sourceTree = ""; }; + 671C338B7259DC5774816885 /* AuthenticationServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationServiceTests.swift; sourceTree = ""; }; 6722709BD6178E10B70C9641 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/SAS.strings; sourceTree = ""; }; 68010886142843705E342645 /* ProgressMaskModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressMaskModifier.swift; sourceTree = ""; }; 6861FE915C7B5466E6962BBA /* StartChatScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreen.swift; sourceTree = ""; }; @@ -1805,6 +1810,7 @@ 8DA1E8F287680C8ED25EDBAC /* NetworkMonitorMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitorMock.swift; sourceTree = ""; }; 8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; path = UITests.xctestplan; sourceTree = ""; }; 8E1584F8BCF407BB94F48F04 /* EncryptionResetPasswordScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetPasswordScreen.swift; sourceTree = ""; }; + 8EAF4A49F3ACD8BB8B0D2371 /* ClientSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientSDKMock.swift; sourceTree = ""; }; 8F21ED7205048668BEB44A38 /* AppActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppActivityView.swift; sourceTree = ""; }; 8F6210134203BE1F2DD5C679 /* RoomDirectoryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectoryCell.swift; sourceTree = ""; }; 8F841F219ACDFC1D3F42FEFB /* RoomChangeRolesScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangeRolesScreenViewModelTests.swift; sourceTree = ""; }; @@ -1871,6 +1877,7 @@ 9CE3C90E487B255B735D73C8 /* RoomScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenViewModel.swift; sourceTree = ""; }; 9CF1EE0AA78470C674554262 /* PillTextAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillTextAttachment.swift; sourceTree = ""; }; 9E6D88E8AFFBF2C1D589C0FA /* UIConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIConstants.swift; sourceTree = ""; }; + 9EB9BA2F30EB8C33226D8FF1 /* UserSessionStoreMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionStoreMock.swift; sourceTree = ""; }; 9ECF11669EF253E98AA2977A /* CompletionSuggestionServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletionSuggestionServiceProtocol.swift; sourceTree = ""; }; 9F1DF3FFFE5ED2B8133F43A7 /* MessageComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposer.swift; sourceTree = ""; }; 9F40FB0A43DAECEC27C73722 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/SAS.strings; sourceTree = ""; }; @@ -1981,6 +1988,7 @@ B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; path = ConfettiScene.scn; sourceTree = ""; }; B63B69F9A2BC74DD40DC75C8 /* AdvancedSettingsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenViewModel.swift; sourceTree = ""; }; B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetails.swift; sourceTree = ""; }; + B655A536341D2695158C6664 /* AuthenticationClientBuilderFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationClientBuilderFactory.swift; sourceTree = ""; }; B6E4AB573FAEBB7B853DD04C /* AppHooks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppHooks.swift; sourceTree = ""; }; B6E89E530A8E92EC44301CA1 /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = ""; }; B70A50C41C5871B4DB905E7E /* VoiceMessageRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomTimelineView.swift; sourceTree = ""; }; @@ -1994,6 +2002,7 @@ B81B6170DB690013CEB646F4 /* MapLibreModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapLibreModels.swift; sourceTree = ""; }; B8516302ACCA94A0E680AB3B /* VoiceMessageButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageButton.swift; sourceTree = ""; }; B858A61F2A570DFB8DE570A7 /* AggregratedReaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AggregratedReaction.swift; sourceTree = ""; }; + B86D447D771CEF6194348F5F /* EventTimelineItemSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTimelineItemSDKMock.swift; sourceTree = ""; }; B8A3B7637DDBD6AA97AC2545 /* CameraPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPicker.swift; sourceTree = ""; }; B8F28602AC7AC881AED37EBA /* NavigationCoordinators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationCoordinators.swift; sourceTree = ""; }; B902EA6CD3296B0E10EE432B /* HomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreen.swift; sourceTree = ""; }; @@ -2138,7 +2147,6 @@ D95E8C0EFEC0C6F96EDAA71A /* PreviewTests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = PreviewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DA14564EE143F73F7E4D1F79 /* RoomNotificationSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenModels.swift; sourceTree = ""; }; DA2AEC1AB349A341FE13DEC1 /* StartChatScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreenUITests.swift; sourceTree = ""; }; - DA38899517F08FE2AF34EB45 /* MockAuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAuthenticationService.swift; sourceTree = ""; }; DA3D82522494E78746B2214E /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/SAS.strings; sourceTree = ""; }; DAB8D7926A5684E18196B538 /* VoiceMessageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageCache.swift; sourceTree = ""; }; DB06F22CFA34885B40976061 /* RoomDetailsEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreen.swift; sourceTree = ""; }; @@ -2887,10 +2895,11 @@ children = ( 69CB8242D69B7E4D0B32E18D /* AggregatedReactionMock.swift */, 3BAC027034248429A438886B /* AppMediatorMock.swift */, + 0554FEA301486A8CFA475D5A /* AuthenticationClientBuilderFactoryMock.swift */, + 4760CE2128FBC217304272AB /* AuthenticationClientBuilderMock.swift */, E2F96CCBEAAA7F2185BFA354 /* ClientProxyMock.swift */, 4E600B315B920B9687F8EE1B /* ComposerDraftServiceMock.swift */, E321E840DCC63790049984F4 /* ElementCallServiceMock.swift */, - 1C7A6BBC686B1F840FA807FB /* EventTimelineItemSDKMock.swift */, 867DC9530C42F7B5176BE465 /* JoinedRoomProxyMock.swift */, 8DA1E8F287680C8ED25EDBAC /* NetworkMonitorMock.swift */, 382B50F7E379B3DBBD174364 /* NotificationSettingsProxyMock.swift */, @@ -2907,7 +2916,9 @@ 7893780A1FD6E3F38B3E9049 /* UserIndicatorControllerMock.swift */, AAD01F7FC2BBAC7351948595 /* UserProfile+Mock.swift */, F4469F6AE311BDC439B3A5EC /* UserSessionMock.swift */, + 9EB9BA2F30EB8C33226D8FF1 /* UserSessionStoreMock.swift */, B23135B06B044CB811139D2F /* Generated */, + E5E545F92D01588360A9BAC5 /* SDK */, ); path = Mocks; sourceTree = ""; @@ -3790,6 +3801,7 @@ 89233612A8632AD7E2803620 /* AudioPlayerStateTests.swift */, C55CC239AE12339C565F6C9A /* AudioRecorderStateTests.swift */, 2441E2424E78A40FC95DBA76 /* AudioRecorderTests.swift */, + 671C338B7259DC5774816885 /* AuthenticationServiceTests.swift */, 8FB89DC7F9A4A91020037001 /* AuthenticationStartScreenViewModelTests.swift */, 93E1FF0DFBB3768F79FDBF6D /* AVMetadataMachineReadableCodeObjectExtensionsTest.swift */, 240610DF32F3213BEC5611D7 /* BlockedUsersScreenViewModelTests.swift */, @@ -4405,7 +4417,6 @@ 295E28C3B9EAADF519BF2F44 /* AuthenticationFlowCoordinatorUITests.swift */, C6FEA87EA3752203065ECE27 /* BugReportUITests.swift */, F8CEB4634C0DD7779C4AB504 /* CreateRoomScreenUITests.swift */, - 1DB34B0C74CD242FED9DD069 /* LoginScreenUITests.swift */, 3368395F06AA180138E185B6 /* PollFormScreenUITests.swift */, C5B7A755E985FA14469E86B2 /* RoomMembersListScreenUITests.swift */, 45571C2EBD98ED7E0CEA7AF7 /* RoomRolesAndPermissionsUITests.swift */, @@ -4651,9 +4662,9 @@ isa = PBXGroup; children = ( 0F569CFB77E0D40BD82203D9 /* AuthenticationClientBuilder.swift */, + B655A536341D2695158C6664 /* AuthenticationClientBuilderFactory.swift */, F3A1AB5A84D843B6AC8D5F1E /* AuthenticationService.swift */, 5E75948AA1FE1D1A7809931F /* AuthenticationServiceProtocol.swift */, - DA38899517F08FE2AF34EB45 /* MockAuthenticationService.swift */, A69869844D2B6F5BD9AABF85 /* OIDCConfigurationProxy.swift */, ); path = Authentication; @@ -5243,6 +5254,15 @@ path = Screens; sourceTree = ""; }; + E5E545F92D01588360A9BAC5 /* SDK */ = { + isa = PBXGroup; + children = ( + 8EAF4A49F3ACD8BB8B0D2371 /* ClientSDKMock.swift */, + B86D447D771CEF6194348F5F /* EventTimelineItemSDKMock.swift */, + ); + path = SDK; + sourceTree = ""; + }; E600AACDF87CDBCE32683236 /* Resources */ = { isa = PBXGroup; children = ( @@ -6072,6 +6092,7 @@ C1429699A6A5BB09A25775C1 /* AudioPlayerStateTests.swift in Sources */, 3042527CB344A9EF1157FC26 /* AudioRecorderStateTests.swift in Sources */, 192A3CDCD0174AD1E4A128E4 /* AudioRecorderTests.swift in Sources */, + 92012C96039BC8C2CAEBA9E2 /* AuthenticationServiceTests.swift in Sources */, 8ED8AF57A06F5EE9978ED23F /* AuthenticationStartScreenViewModelTests.swift in Sources */, CEAEA57B7665C8E790599A78 /* BlockedUsersScreenViewModelTests.swift in Sources */, 1B2F9F368619FFF8C63C87CC /* BugReportScreenViewModelTests.swift in Sources */, @@ -6278,6 +6299,9 @@ 7BD2123144A32F082CECC108 /* AudioRoomTimelineView.swift in Sources */, 9278EC51D24E57445B290521 /* AudioSessionProtocol.swift in Sources */, 8A6CB15C8FC68F557750BF54 /* AuthenticationClientBuilder.swift in Sources */, + 210DB40676DF2A23E69C2D06 /* AuthenticationClientBuilderFactory.swift in Sources */, + A51C65E5A3C9F2464A91A380 /* AuthenticationClientBuilderFactoryMock.swift in Sources */, + 0F4709282FCCFBEFED427B8A /* AuthenticationClientBuilderMock.swift in Sources */, 67E9926C4572C54F59FCA91A /* AuthenticationFlowCoordinator.swift in Sources */, 9847B056C1A216C314D21E68 /* AuthenticationService.swift in Sources */, 56DACDD379A86A1F5DEFE7BE /* AuthenticationServiceProtocol.swift in Sources */, @@ -6333,6 +6357,7 @@ 1950A80CD198BED283DFC2CE /* ClientProxy.swift in Sources */, DDFBDEE1DC32BDD5488F898C /* ClientProxyMock.swift in Sources */, 24BDDD09A90B8BFE3793F3AA /* ClientProxyProtocol.swift in Sources */, + 41C5DA0C06F30311A221E85B /* ClientSDKMock.swift in Sources */, 0C797CD650DFD2876BEC5173 /* CollapsibleReactionLayout.swift in Sources */, 78A3D84BA47DAC69B4D0A34C /* CollapsibleRoomTimelineView.swift in Sources */, 0DC815CA24E1BD7F408F37D3 /* CollapsibleTimelineItem.swift in Sources */, @@ -6423,7 +6448,7 @@ 50539366B408780B232C1910 /* EstimatedWaveformView.swift in Sources */, F78BAD28482A467287A9A5A3 /* EventBasedMessageTimelineItemProtocol.swift in Sources */, 02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */, - EE57A96130DD8DB053790AE2 /* EventTimelineItemSDKMock.swift in Sources */, + 5BBDF9926CB645DE2F7BC258 /* EventTimelineItemSDKMock.swift in Sources */, 63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */, 5F06AD3C66884CE793AE6119 /* FileManager.swift in Sources */, D33AC79A50DFC26D2498DD28 /* FileRoomTimelineItem.swift in Sources */, @@ -6562,7 +6587,6 @@ F54E2D6CAD96E1AC15BC526F /* MessageForwardingScreenViewModel.swift in Sources */, C13128AAA787A4C2CBE4EE82 /* MessageForwardingScreenViewModelProtocol.swift in Sources */, C97325EFDCCEE457432A9E82 /* MessageText.swift in Sources */, - 32FC143630CE22A9E403370B /* MockAuthenticationService.swift in Sources */, B659E3A49889E749E3239EA7 /* MockMediaProvider.swift in Sources */, 09C83DDDB07C28364F325209 /* MockRoomTimelineController.swift in Sources */, B721125D17A0BA86794F29FB /* MockServerSelectionScreenState.swift in Sources */, @@ -6982,6 +7006,7 @@ 6586E1F1D5F0651D0638FFAF /* UserSessionMock.swift in Sources */, 978BB24F2A5D31EE59EEC249 /* UserSessionProtocol.swift in Sources */, 7E91BAC17963ED41208F489B /* UserSessionStore.swift in Sources */, + 79D57E9AE03A2DC689D14EA2 /* UserSessionStoreMock.swift in Sources */, AC69B6DF15FC451AB2945036 /* UserSessionStoreProtocol.swift in Sources */, F07D88421A9BC4D03D4A5055 /* VideoRoomTimelineItem.swift in Sources */, 1A83DD22F3E6F76B13B6E2F9 /* VideoRoomTimelineItemContent.swift in Sources */, @@ -7026,7 +7051,6 @@ 7756C4E90CABE6F14F7920A0 /* BugReportUITests.swift in Sources */, 94D0F36A87E596A93C0C178A /* Bundle.swift in Sources */, 9F19096BFA629C0AC282B1E4 /* CreateRoomScreenUITests.swift in Sources */, - 5C8AFBF168A41E20835F3B86 /* LoginScreenUITests.swift in Sources */, 0CF81807BE5FBFC9E2BBCECF /* PollFormScreenUITests.swift in Sources */, 44121202B4A260C98BF615A7 /* RoomMembersListScreenUITests.swift in Sources */, D29E046C1E3045E0346C479D /* RoomRolesAndPermissionsUITests.swift in Sources */, diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index c979c0035b..18f366bea9 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -485,7 +485,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg encryptionKeyProvider: EncryptionKeyProvider(), appSettings: appSettings, appHooks: appHooks) - _ = await authenticationService.configure(for: userSession.clientProxy.homeserver) + _ = await authenticationService.configure(for: userSession.clientProxy.homeserver, flow: .login) let parameters = SoftLogoutScreenCoordinatorParameters(authenticationService: authenticationService, credentials: credentials, diff --git a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift index 996ce5b743..f2cf5c9f47 100644 --- a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift @@ -86,11 +86,11 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { switch action { case .loginManually: - Task { await self.startAuthentication(flow: .login) } + showServerConfirmationScreen(authenticationFlow: .login) case .loginWithQR: startQRCodeLogin() case .register: - Task { await self.startAuthentication(flow: .register) } + showServerConfirmationScreen(authenticationFlow: .register) case .reportProblem: showReportProblemScreen() } @@ -113,7 +113,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { switch action { case .signInManually: navigationStackCoordinator.setSheetCoordinator(nil) - Task { await self.startAuthentication(flow: .login) } + showServerConfirmationScreen(authenticationFlow: .login) case .cancel: navigationStackCoordinator.setSheetCoordinator(nil) case .done(let userSession): @@ -137,25 +137,14 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { bugReportFlowCoordinator?.start() } - private func startAuthentication(flow: AuthenticationFlow) async { - startLoading() - - switch await authenticationService.configure(for: appSettings.defaultHomeserverAddress) { - case .success: - stopLoading() - showServerConfirmationScreen(authenticationFlow: flow) - case .failure: - stopLoading() - showServerSelectionScreen(authenticationFlow: flow, isModallyPresented: false) - } - } - - private func showServerSelectionScreen(authenticationFlow: AuthenticationFlow, isModallyPresented: Bool) { + // TODO: Move this method after showServerConfirmationScreen + private func showServerSelectionScreen(authenticationFlow: AuthenticationFlow) { let navigationCoordinator = NavigationStackCoordinator() let parameters = ServerSelectionScreenCoordinatorParameters(authenticationService: authenticationService, - userIndicatorController: userIndicatorController, - isModallyPresented: isModallyPresented) + authenticationFlow: authenticationFlow, + slidingSyncLearnMoreURL: appSettings.slidingSyncLearnMoreURL, + userIndicatorController: userIndicatorController) let coordinator = ServerSelectionScreenCoordinator(parameters: parameters) coordinator.actions @@ -164,42 +153,26 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { switch action { case .updated: - if isModallyPresented { - navigationStackCoordinator.setSheetCoordinator(nil) - } else { - // We are here because the default server failed to respond. - if authenticationService.homeserver.value.loginMode == .password { - if authenticationFlow == .login { - // Add the password login screen directly to the flow, its fine. - showLoginScreen() - } else { - // Add the web registration screen directly to the flow, its fine. - showWebRegistration() - } - } else { - // OIDC is presented from the confirmation screen so replace the - // server selection screen which was inserted to handle the failure. - navigationStackCoordinator.pop() - showServerConfirmationScreen(authenticationFlow: authenticationFlow) - } - } + navigationStackCoordinator.setSheetCoordinator(nil) case .dismiss: navigationStackCoordinator.setSheetCoordinator(nil) } } .store(in: &cancellables) - if isModallyPresented { - navigationCoordinator.setRootCoordinator(coordinator) - navigationStackCoordinator.setSheetCoordinator(navigationCoordinator) - } else { - navigationStackCoordinator.push(coordinator) - } + navigationCoordinator.setRootCoordinator(coordinator) + navigationStackCoordinator.setSheetCoordinator(navigationCoordinator) } private func showServerConfirmationScreen(authenticationFlow: AuthenticationFlow) { + // Reset the service back to the default homeserver before continuing. This ensures + // we check that registration is supported if it was previously configured for login. + authenticationService.reset() + let parameters = ServerConfirmationScreenCoordinatorParameters(authenticationService: authenticationService, - authenticationFlow: authenticationFlow) + authenticationFlow: authenticationFlow, + slidingSyncLearnMoreURL: appSettings.slidingSyncLearnMoreURL, + userIndicatorController: userIndicatorController) let coordinator = ServerConfirmationScreenCoordinator(parameters: parameters) coordinator.actions.sink { [weak self] action in @@ -215,7 +188,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { showLoginScreen() } case .changeServer: - showServerSelectionScreen(authenticationFlow: authenticationFlow, isModallyPresented: true) + showServerSelectionScreen(authenticationFlow: authenticationFlow) } } .store(in: &cancellables) diff --git a/ElementX/Sources/Mocks/AuthenticationClientBuilderFactoryMock.swift b/ElementX/Sources/Mocks/AuthenticationClientBuilderFactoryMock.swift new file mode 100644 index 0000000000..841a0701d3 --- /dev/null +++ b/ElementX/Sources/Mocks/AuthenticationClientBuilderFactoryMock.swift @@ -0,0 +1,22 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Foundation +import MatrixRustSDK + +extension AuthenticationClientBuilderFactoryMock { + struct Configuration { + var builderConfiguration: AuthenticationClientBuilderMock.Configuration = .init() + } + + convenience init(configuration: Configuration) { + self.init() + + let clientBuilder = AuthenticationClientBuilderMock(configuration: configuration.builderConfiguration) + makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReturnValue = clientBuilder + } +} diff --git a/ElementX/Sources/Mocks/AuthenticationClientBuilderMock.swift b/ElementX/Sources/Mocks/AuthenticationClientBuilderMock.swift new file mode 100644 index 0000000000..0cccf9e539 --- /dev/null +++ b/ElementX/Sources/Mocks/AuthenticationClientBuilderMock.swift @@ -0,0 +1,47 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Foundation +import MatrixRustSDK + +extension AuthenticationClientBuilderMock { + struct Configuration { + var homeserverClients = [ + "matrix.org": ClientSDKMock(configuration: .init()), + "example.com": ClientSDKMock(configuration: .init(serverAddress: "example.com", + homeserverURL: "https://matrix.example.com", + slidingSyncVersion: .native, + supportsPasswordLogin: true, + elementWellKnown: "")), + "company.com": ClientSDKMock(configuration: .init(serverAddress: "company.com", + homeserverURL: "https://matrix.company.com", + slidingSyncVersion: .native, + oidcLoginURL: "https://auth.company.com/oidc", + supportsPasswordLogin: false, + elementWellKnown: "")), + "server.net": ClientSDKMock(configuration: .init(serverAddress: "server.net", + homeserverURL: "https://matrix.example.com", + slidingSyncVersion: .native, + supportsPasswordLogin: false, + elementWellKnown: "")) + ] + var qrCodeClient = ClientSDKMock(configuration: .init()) + } + + convenience init(configuration: Configuration) { + self.init() + + buildHomeserverAddressClosure = { address in + guard let client = configuration.homeserverClients[address] else { + throw ClientBuildError.ServerUnreachable(message: "Not a known homeserver.") + } + return client + } + + buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerReturnValue = configuration.qrCodeClient + } +} diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index ba521052fb..1cc39fdd50 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -1819,6 +1819,230 @@ class AudioSessionMock: AudioSessionProtocol { try setActiveOptionsClosure?(active, options) } } +class AuthenticationClientBuilderFactoryMock: AuthenticationClientBuilderFactoryProtocol { + + //MARK: - makeBuilder + + var makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingCallsCount = 0 + var makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount: Int { + get { + if Thread.isMainThread { + return makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingCallsCount = newValue + } + } + } + } + var makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCalled: Bool { + return makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount > 0 + } + var makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReceivedArguments: (sessionDirectories: SessionDirectories, passphrase: String, clientSessionDelegate: ClientSessionDelegate, appSettings: AppSettings, appHooks: AppHooks)? + var makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReceivedInvocations: [(sessionDirectories: SessionDirectories, passphrase: String, clientSessionDelegate: ClientSessionDelegate, appSettings: AppSettings, appHooks: AppHooks)] = [] + + var makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingReturnValue: AuthenticationClientBuilderProtocol! + var makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReturnValue: AuthenticationClientBuilderProtocol! { + get { + if Thread.isMainThread { + return makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingReturnValue + } else { + var returnValue: AuthenticationClientBuilderProtocol? = nil + DispatchQueue.main.sync { + returnValue = makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksUnderlyingReturnValue = newValue + } + } + } + } + var makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksClosure: ((SessionDirectories, String, ClientSessionDelegate, AppSettings, AppHooks) -> AuthenticationClientBuilderProtocol)? + + func makeBuilder(sessionDirectories: SessionDirectories, passphrase: String, clientSessionDelegate: ClientSessionDelegate, appSettings: AppSettings, appHooks: AppHooks) -> AuthenticationClientBuilderProtocol { + makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount += 1 + makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReceivedArguments = (sessionDirectories: sessionDirectories, passphrase: passphrase, clientSessionDelegate: clientSessionDelegate, appSettings: appSettings, appHooks: appHooks) + DispatchQueue.main.async { + self.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReceivedInvocations.append((sessionDirectories: sessionDirectories, passphrase: passphrase, clientSessionDelegate: clientSessionDelegate, appSettings: appSettings, appHooks: appHooks)) + } + if let makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksClosure = makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksClosure { + return makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksClosure(sessionDirectories, passphrase, clientSessionDelegate, appSettings, appHooks) + } else { + return makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksReturnValue + } + } +} +class AuthenticationClientBuilderMock: AuthenticationClientBuilderProtocol { + + //MARK: - build + + var buildHomeserverAddressThrowableError: Error? + var buildHomeserverAddressUnderlyingCallsCount = 0 + var buildHomeserverAddressCallsCount: Int { + get { + if Thread.isMainThread { + return buildHomeserverAddressUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = buildHomeserverAddressUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + buildHomeserverAddressUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + buildHomeserverAddressUnderlyingCallsCount = newValue + } + } + } + } + var buildHomeserverAddressCalled: Bool { + return buildHomeserverAddressCallsCount > 0 + } + var buildHomeserverAddressReceivedHomeserverAddress: String? + var buildHomeserverAddressReceivedInvocations: [String] = [] + + var buildHomeserverAddressUnderlyingReturnValue: ClientProtocol! + var buildHomeserverAddressReturnValue: ClientProtocol! { + get { + if Thread.isMainThread { + return buildHomeserverAddressUnderlyingReturnValue + } else { + var returnValue: ClientProtocol? = nil + DispatchQueue.main.sync { + returnValue = buildHomeserverAddressUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + buildHomeserverAddressUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + buildHomeserverAddressUnderlyingReturnValue = newValue + } + } + } + } + var buildHomeserverAddressClosure: ((String) async throws -> ClientProtocol)? + + func build(homeserverAddress: String) async throws -> ClientProtocol { + if let error = buildHomeserverAddressThrowableError { + throw error + } + buildHomeserverAddressCallsCount += 1 + buildHomeserverAddressReceivedHomeserverAddress = homeserverAddress + DispatchQueue.main.async { + self.buildHomeserverAddressReceivedInvocations.append(homeserverAddress) + } + if let buildHomeserverAddressClosure = buildHomeserverAddressClosure { + return try await buildHomeserverAddressClosure(homeserverAddress) + } else { + return buildHomeserverAddressReturnValue + } + } + //MARK: - buildWithQRCode + + var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerThrowableError: Error? + var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingCallsCount = 0 + var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerCallsCount: Int { + get { + if Thread.isMainThread { + return buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingCallsCount = newValue + } + } + } + } + var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerCalled: Bool { + return buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerCallsCount > 0 + } + var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerReceivedArguments: (qrCodeData: QrCodeData, oidcConfiguration: OIDCConfigurationProxy, progressListener: QrLoginProgressListenerProxy)? + var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerReceivedInvocations: [(qrCodeData: QrCodeData, oidcConfiguration: OIDCConfigurationProxy, progressListener: QrLoginProgressListenerProxy)] = [] + + var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingReturnValue: ClientProtocol! + var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerReturnValue: ClientProtocol! { + get { + if Thread.isMainThread { + return buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingReturnValue + } else { + var returnValue: ClientProtocol? = nil + DispatchQueue.main.sync { + returnValue = buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerUnderlyingReturnValue = newValue + } + } + } + } + var buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerClosure: ((QrCodeData, OIDCConfigurationProxy, QrLoginProgressListenerProxy) async throws -> ClientProtocol)? + + func buildWithQRCode(qrCodeData: QrCodeData, oidcConfiguration: OIDCConfigurationProxy, progressListener: QrLoginProgressListenerProxy) async throws -> ClientProtocol { + if let error = buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerThrowableError { + throw error + } + buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerCallsCount += 1 + buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerReceivedArguments = (qrCodeData: qrCodeData, oidcConfiguration: oidcConfiguration, progressListener: progressListener) + DispatchQueue.main.async { + self.buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerReceivedInvocations.append((qrCodeData: qrCodeData, oidcConfiguration: oidcConfiguration, progressListener: progressListener)) + } + if let buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerClosure = buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerClosure { + return try await buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerClosure(qrCodeData, oidcConfiguration, progressListener) + } else { + return buildWithQRCodeQrCodeDataOidcConfigurationProgressListenerReturnValue + } + } +} class BugReportServiceMock: BugReportServiceProtocol { var crashedLastRun: Bool { get { return underlyingCrashedLastRun } @@ -15296,6 +15520,271 @@ class UserSessionMock: UserSessionProtocol { var underlyingCallbacks: PassthroughSubject! } +class UserSessionStoreMock: UserSessionStoreProtocol { + var hasSessions: Bool { + get { return underlyingHasSessions } + set(value) { underlyingHasSessions = value } + } + var underlyingHasSessions: Bool! + var userIDs: [String] = [] + var clientSessionDelegate: ClientSessionDelegate { + get { return underlyingClientSessionDelegate } + set(value) { underlyingClientSessionDelegate = value } + } + var underlyingClientSessionDelegate: ClientSessionDelegate! + + //MARK: - reset + + var resetUnderlyingCallsCount = 0 + var resetCallsCount: Int { + get { + if Thread.isMainThread { + return resetUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = resetUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + resetUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + resetUnderlyingCallsCount = newValue + } + } + } + } + var resetCalled: Bool { + return resetCallsCount > 0 + } + var resetClosure: (() -> Void)? + + func reset() { + resetCallsCount += 1 + resetClosure?() + } + //MARK: - restoreUserSession + + var restoreUserSessionUnderlyingCallsCount = 0 + var restoreUserSessionCallsCount: Int { + get { + if Thread.isMainThread { + return restoreUserSessionUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = restoreUserSessionUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + restoreUserSessionUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + restoreUserSessionUnderlyingCallsCount = newValue + } + } + } + } + var restoreUserSessionCalled: Bool { + return restoreUserSessionCallsCount > 0 + } + + var restoreUserSessionUnderlyingReturnValue: Result! + var restoreUserSessionReturnValue: Result! { + get { + if Thread.isMainThread { + return restoreUserSessionUnderlyingReturnValue + } else { + var returnValue: Result? = nil + DispatchQueue.main.sync { + returnValue = restoreUserSessionUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + restoreUserSessionUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + restoreUserSessionUnderlyingReturnValue = newValue + } + } + } + } + var restoreUserSessionClosure: (() async -> Result)? + + func restoreUserSession() async -> Result { + restoreUserSessionCallsCount += 1 + if let restoreUserSessionClosure = restoreUserSessionClosure { + return await restoreUserSessionClosure() + } else { + return restoreUserSessionReturnValue + } + } + //MARK: - userSession + + var userSessionForSessionDirectoriesPassphraseUnderlyingCallsCount = 0 + var userSessionForSessionDirectoriesPassphraseCallsCount: Int { + get { + if Thread.isMainThread { + return userSessionForSessionDirectoriesPassphraseUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = userSessionForSessionDirectoriesPassphraseUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + userSessionForSessionDirectoriesPassphraseUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + userSessionForSessionDirectoriesPassphraseUnderlyingCallsCount = newValue + } + } + } + } + var userSessionForSessionDirectoriesPassphraseCalled: Bool { + return userSessionForSessionDirectoriesPassphraseCallsCount > 0 + } + var userSessionForSessionDirectoriesPassphraseReceivedArguments: (client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String?)? + var userSessionForSessionDirectoriesPassphraseReceivedInvocations: [(client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String?)] = [] + + var userSessionForSessionDirectoriesPassphraseUnderlyingReturnValue: Result! + var userSessionForSessionDirectoriesPassphraseReturnValue: Result! { + get { + if Thread.isMainThread { + return userSessionForSessionDirectoriesPassphraseUnderlyingReturnValue + } else { + var returnValue: Result? = nil + DispatchQueue.main.sync { + returnValue = userSessionForSessionDirectoriesPassphraseUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + userSessionForSessionDirectoriesPassphraseUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + userSessionForSessionDirectoriesPassphraseUnderlyingReturnValue = newValue + } + } + } + } + var userSessionForSessionDirectoriesPassphraseClosure: ((ClientProtocol, SessionDirectories, String?) async -> Result)? + + func userSession(for client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String?) async -> Result { + userSessionForSessionDirectoriesPassphraseCallsCount += 1 + userSessionForSessionDirectoriesPassphraseReceivedArguments = (client: client, sessionDirectories: sessionDirectories, passphrase: passphrase) + DispatchQueue.main.async { + self.userSessionForSessionDirectoriesPassphraseReceivedInvocations.append((client: client, sessionDirectories: sessionDirectories, passphrase: passphrase)) + } + if let userSessionForSessionDirectoriesPassphraseClosure = userSessionForSessionDirectoriesPassphraseClosure { + return await userSessionForSessionDirectoriesPassphraseClosure(client, sessionDirectories, passphrase) + } else { + return userSessionForSessionDirectoriesPassphraseReturnValue + } + } + //MARK: - logout + + var logoutUserSessionUnderlyingCallsCount = 0 + var logoutUserSessionCallsCount: Int { + get { + if Thread.isMainThread { + return logoutUserSessionUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = logoutUserSessionUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + logoutUserSessionUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + logoutUserSessionUnderlyingCallsCount = newValue + } + } + } + } + var logoutUserSessionCalled: Bool { + return logoutUserSessionCallsCount > 0 + } + var logoutUserSessionReceivedUserSession: UserSessionProtocol? + var logoutUserSessionReceivedInvocations: [UserSessionProtocol] = [] + var logoutUserSessionClosure: ((UserSessionProtocol) -> Void)? + + func logout(userSession: UserSessionProtocol) { + logoutUserSessionCallsCount += 1 + logoutUserSessionReceivedUserSession = userSession + DispatchQueue.main.async { + self.logoutUserSessionReceivedInvocations.append(userSession) + } + logoutUserSessionClosure?(userSession) + } + //MARK: - clearCache + + var clearCacheForUnderlyingCallsCount = 0 + var clearCacheForCallsCount: Int { + get { + if Thread.isMainThread { + return clearCacheForUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = clearCacheForUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + clearCacheForUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + clearCacheForUnderlyingCallsCount = newValue + } + } + } + } + var clearCacheForCalled: Bool { + return clearCacheForCallsCount > 0 + } + var clearCacheForReceivedUserID: String? + var clearCacheForReceivedInvocations: [String] = [] + var clearCacheForClosure: ((String) -> Void)? + + func clearCache(for userID: String) { + clearCacheForCallsCount += 1 + clearCacheForReceivedUserID = userID + DispatchQueue.main.async { + self.clearCacheForReceivedInvocations.append(userID) + } + clearCacheForClosure?(userID) + } +} class VoiceMessageCacheMock: VoiceMessageCacheProtocol { var urlForRecording: URL { get { return underlyingUrlForRecording } diff --git a/ElementX/Sources/Mocks/SDK/ClientSDKMock.swift b/ElementX/Sources/Mocks/SDK/ClientSDKMock.swift new file mode 100644 index 0000000000..61738a095f --- /dev/null +++ b/ElementX/Sources/Mocks/SDK/ClientSDKMock.swift @@ -0,0 +1,75 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Foundation +import MatrixRustSDK + +extension ClientSDKMock { + struct Configuration { + // MARK: Authentication + + var serverAddress = "matrix.org" + var homeserverURL = "https://matrix-client.matrix.org" + var slidingSyncVersion = SlidingSyncVersion.native + var oidcLoginURL: String? + var supportsPasswordLogin = true + var elementWellKnown = "{\"registration_helper_url\":\"https://develop.element.io/#/mobile_register\"}" + var validCredentials = (username: "alice", password: "12345678") + + // MARK: Session + + var userID: String? + var session = Session(accessToken: UUID().uuidString, + refreshToken: nil, + userId: "@alice:matrix.org", + deviceId: UUID().uuidString, + homeserverUrl: "https://matrix-client.matrix.org", + oidcData: nil, + slidingSyncVersion: .native) + } + + enum MockError: Error { case generic } + + convenience init(configuration: Configuration) { + self.init() + + homeserverLoginDetailsReturnValue = HomeserverLoginDetailsSDKMock(configuration: configuration) + slidingSyncVersionReturnValue = configuration.slidingSyncVersion + userIdServerNameThrowableError = MockError.generic + serverReturnValue = "https://\(configuration.serverAddress)" + getUrlUrlReturnValue = configuration.elementWellKnown + urlForOidcLoginOidcConfigurationReturnValue = OidcAuthorizationDataSDKMock(configuration: configuration) + loginUsernamePasswordInitialDeviceNameDeviceIdClosure = { username, password, _, _ in + guard username == configuration.validCredentials.username, + password == configuration.validCredentials.password else { + throw MockError.generic // use the matrix error + } + } + + userIdReturnValue = configuration.userID + sessionReturnValue = configuration.session + } +} + +extension HomeserverLoginDetailsSDKMock { + convenience init(configuration: ClientSDKMock.Configuration) { + self.init() + + slidingSyncVersionReturnValue = configuration.slidingSyncVersion + supportsPasswordLoginReturnValue = configuration.supportsPasswordLogin + supportsOidcLoginReturnValue = configuration.oidcLoginURL != nil + urlReturnValue = configuration.homeserverURL + } +} + +extension OidcAuthorizationDataSDKMock { + convenience init(configuration: ClientSDKMock.Configuration) { + self.init() + + loginUrlReturnValue = configuration.oidcLoginURL + } +} diff --git a/ElementX/Sources/Mocks/EventTimelineItemSDKMock.swift b/ElementX/Sources/Mocks/SDK/EventTimelineItemSDKMock.swift similarity index 100% rename from ElementX/Sources/Mocks/EventTimelineItemSDKMock.swift rename to ElementX/Sources/Mocks/SDK/EventTimelineItemSDKMock.swift diff --git a/ElementX/Sources/Mocks/UserSessionStoreMock.swift b/ElementX/Sources/Mocks/UserSessionStoreMock.swift new file mode 100644 index 0000000000..69b8418f1c --- /dev/null +++ b/ElementX/Sources/Mocks/UserSessionStoreMock.swift @@ -0,0 +1,17 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +extension UserSessionStoreMock { + struct Configuration { } + + convenience init(configuration: Configuration) { + self.init() + + userSessionForSessionDirectoriesPassphraseReturnValue = .success(UserSessionMock(.init(clientProxy: ClientProxyMock(.init())))) + clientSessionDelegate = KeychainControllerMock() + } +} diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginHomeserver.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginHomeserver.swift index 5bfb33da79..9f3e1fc763 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginHomeserver.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginHomeserver.swift @@ -25,6 +25,11 @@ struct LoginHomeserver: Equatable { self.registrationHelperURL = registrationHelperURL } + /// Whether or not the app is able to register on this homeserver. + var supportsRegistration: Bool { + loginMode == .oidc || (address == "matrix.org" && registrationHelperURL != nil) + } + /// Sanitizes a user entered homeserver address with the following rules /// - Trim any whitespace. /// - Lowercase the address. diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenCoordinator.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenCoordinator.swift index 9c6d3e2be4..fbbb7409ba 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenCoordinator.swift @@ -145,7 +145,7 @@ final class LoginScreenCoordinator: CoordinatorProtocol { startLoading(isInteractionBlocking: false) Task { - switch await authenticationService.configure(for: homeserverDomain) { + switch await authenticationService.configure(for: homeserverDomain, flow: .login) { case .success: stopLoading() if authenticationService.homeserver.value.loginMode == .oidc { diff --git a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenCoordinator.swift b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenCoordinator.swift index 37bd5f4845..6d13d835f7 100644 --- a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenCoordinator.swift @@ -11,6 +11,8 @@ import SwiftUI struct ServerConfirmationScreenCoordinatorParameters { let authenticationService: AuthenticationServiceProtocol let authenticationFlow: AuthenticationFlow + let slidingSyncLearnMoreURL: URL + let userIndicatorController: UserIndicatorControllerProtocol } enum ServerConfirmationScreenCoordinatorAction { @@ -29,7 +31,9 @@ final class ServerConfirmationScreenCoordinator: CoordinatorProtocol { init(parameters: ServerConfirmationScreenCoordinatorParameters) { viewModel = ServerConfirmationScreenViewModel(authenticationService: parameters.authenticationService, - authenticationFlow: parameters.authenticationFlow) + authenticationFlow: parameters.authenticationFlow, + slidingSyncLearnMoreURL: parameters.slidingSyncLearnMoreURL, + userIndicatorController: parameters.userIndicatorController) } func start() { diff --git a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenModels.swift b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenModels.swift index d456b9ebaa..811a7fa676 100644 --- a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenModels.swift +++ b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenModels.swift @@ -19,11 +19,11 @@ struct ServerConfirmationScreenViewState: BindableState { var homeserverAddress: String /// The flow being attempted on the selected homeserver. let authenticationFlow: AuthenticationFlow - /// Whether or not the homeserver supports registration. - var homeserverSupportsRegistration = false /// The presentation anchor used for OIDC authentication. var window: UIWindow? + var bindings = ServerConfirmationScreenBindings() + /// The screen's title. var title: String { switch authenticationFlow { @@ -46,23 +46,16 @@ struct ServerConfirmationScreenViewState: BindableState { "" } case .register: - if canContinue { - L10n.screenServerConfirmationMessageRegister - } else { - L10n.errorAccountCreationNotPossible - } - } - } - - /// Whether or not it is valid to continue the flow. - var canContinue: Bool { - switch authenticationFlow { - case .login: true - case .register: homeserverSupportsRegistration + L10n.screenServerConfirmationMessageRegister } } } +struct ServerConfirmationScreenBindings { + /// Information describing the currently displayed alert. + var alertInfo: AlertInfo? +} + enum ServerConfirmationScreenViewAction { /// Updates the window used as the OIDC presentation anchor. case updateWindow(UIWindow) @@ -71,3 +64,16 @@ enum ServerConfirmationScreenViewAction { /// The user would like to change to a different homeserver. case changeServer } + +enum ServerConfirmationScreenAlert: Hashable { + /// An alert that informs the user that a server could not be found. + case homeserverNotFound + /// An alert that informs the user about a bad well-known file. + case invalidWellKnown(String) + /// An alert that allows the user to learn about sliding sync. + case slidingSync + /// An alert that informs the user that registration isn't supported. + case registration + /// An unknown error has occurred. + case unknownError +} diff --git a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenViewModel.swift b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenViewModel.swift index 717c8f76fc..29563103da 100644 --- a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenViewModel.swift +++ b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenViewModel.swift @@ -11,46 +11,118 @@ import SwiftUI typealias ServerConfirmationScreenViewModelType = StateStoreViewModel class ServerConfirmationScreenViewModel: ServerConfirmationScreenViewModelType, ServerConfirmationScreenViewModelProtocol { + let authenticationService: AuthenticationServiceProtocol + let authenticationFlow: AuthenticationFlow + let slidingSyncLearnMoreURL: URL + let userIndicatorController: UserIndicatorControllerProtocol + private var actionsSubject: PassthroughSubject = .init() var actions: AnyPublisher { actionsSubject.eraseToAnyPublisher() } - init(authenticationService: AuthenticationServiceProtocol, authenticationFlow: AuthenticationFlow) { - let homeserver = authenticationService.homeserver.value + init(authenticationService: AuthenticationServiceProtocol, + authenticationFlow: AuthenticationFlow, + slidingSyncLearnMoreURL: URL, + userIndicatorController: UserIndicatorControllerProtocol) { + self.authenticationService = authenticationService + self.authenticationFlow = authenticationFlow + self.slidingSyncLearnMoreURL = slidingSyncLearnMoreURL + self.userIndicatorController = userIndicatorController + let homeserver = authenticationService.homeserver.value super.init(initialViewState: ServerConfirmationScreenViewState(homeserverAddress: homeserver.address, - authenticationFlow: authenticationFlow, - homeserverSupportsRegistration: homeserver.supportsRegistration)) + authenticationFlow: authenticationFlow)) authenticationService.homeserver .receive(on: DispatchQueue.main) .sink { [weak self] homeserver in guard let self else { return } state.homeserverAddress = homeserver.address - state.homeserverSupportsRegistration = homeserver.supportsRegistration } .store(in: &cancellables) } - // MARK: - Public - override func process(viewAction: ServerConfirmationScreenViewAction) { switch viewAction { case .updateWindow(let window): guard state.window != window else { return } Task { state.window = window } case .confirm: - actionsSubject.send(.confirm) + Task { await configureAndContinue() } case .changeServer: actionsSubject.send(.changeServer) } } -} - -extension LoginHomeserver { - var supportsRegistration: Bool { - loginMode == .oidc || (address == "matrix.org" && registrationHelperURL != nil) + + // MARK: - Private + + private func configureAndContinue() async { + let homeserver = authenticationService.homeserver.value + + // If the login mode is unknown, the service hasn't be configured and we need to do it now. + // Otherwise we can continue the flow as server selection has been performed and succeeded. + guard homeserver.loginMode == .unknown || authenticationService.flow != authenticationFlow else { + actionsSubject.send(.confirm) + return + } + + startLoading() + defer { stopLoading() } + + switch await authenticationService.configure(for: homeserver.address, flow: authenticationFlow) { + case .success: + actionsSubject.send(.confirm) + case .failure(let error): + switch error { + case .invalidServer, .invalidHomeserverAddress: + displayError(.homeserverNotFound) + case .invalidWellKnown(let error): + displayError(.invalidWellKnown(error)) + case .slidingSyncNotAvailable: + displayError(.slidingSync) + case .registrationNotSupported: + displayError(.registration) + default: + displayError(.unknownError) + } + } + } + + private func startLoading(label: String = L10n.commonLoading) { + userIndicatorController.submitIndicator(UserIndicator(type: .modal, + title: label, + persistent: true)) + } + + private func stopLoading() { + userIndicatorController.retractAllIndicators() + } + + private func displayError(_ type: ServerConfirmationScreenAlert) { + switch type { + case .homeserverNotFound: + state.bindings.alertInfo = AlertInfo(id: .homeserverNotFound, + title: L10n.errorUnknown, + message: L10n.screenChangeServerErrorInvalidHomeserver) + case .invalidWellKnown(let error): + state.bindings.alertInfo = AlertInfo(id: .invalidWellKnown(error), + title: L10n.commonServerNotSupported, + message: L10n.screenChangeServerErrorInvalidWellKnown(error)) + case .slidingSync: + let openURL = { UIApplication.shared.open(self.slidingSyncLearnMoreURL) } + state.bindings.alertInfo = AlertInfo(id: .slidingSync, + title: L10n.commonServerNotSupported, + message: L10n.screenChangeServerErrorNoSlidingSyncMessage, + primaryButton: .init(title: L10n.actionLearnMore, role: .cancel, action: openURL), + secondaryButton: .init(title: L10n.actionCancel, action: nil)) + case .registration: + state.bindings.alertInfo = AlertInfo(id: .registration, + title: L10n.errorUnknown, + message: L10n.errorAccountCreationNotPossible) + case .unknownError: + state.bindings.alertInfo = AlertInfo(id: .unknownError) + } } } diff --git a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/View/ServerConfirmationScreen.swift b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/View/ServerConfirmationScreen.swift index 78fbed4289..14beaed6f9 100644 --- a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/View/ServerConfirmationScreen.swift +++ b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/View/ServerConfirmationScreen.swift @@ -19,6 +19,7 @@ struct ServerConfirmationScreen: View { } .background() .backgroundStyle(.compound.bgCanvasDefault) + .alert(item: $context.alertInfo) .introspect(.window, on: .supportedVersions) { window in context.send(viewAction: .updateWindow(window)) } @@ -53,7 +54,6 @@ struct ServerConfirmationScreen: View { } .buttonStyle(.compound(.primary)) .accessibilityIdentifier(A11yIdentifiers.serverConfirmationScreen.continue) - .disabled(!context.viewState.canContinue) Button { context.send(viewAction: .changeServer) } label: { Text(L10n.screenServerConfirmationChangeServer) @@ -68,10 +68,8 @@ struct ServerConfirmationScreen: View { // MARK: - Previews struct ServerConfirmationScreen_Previews: PreviewProvider, TestablePreview { - static let loginViewModel = ServerConfirmationScreenViewModel(authenticationService: MockAuthenticationService(), - authenticationFlow: .login) - static let registerViewModel = ServerConfirmationScreenViewModel(authenticationService: MockAuthenticationService(), - authenticationFlow: .register) + static let loginViewModel = makeViewModel(flow: .login) + static let registerViewModel = makeViewModel(flow: .register) static var previews: some View { NavigationStack { @@ -86,4 +84,11 @@ struct ServerConfirmationScreen_Previews: PreviewProvider, TestablePreview { } .previewDisplayName("Register") } + + static func makeViewModel(flow: AuthenticationFlow) -> ServerConfirmationScreenViewModel { + ServerConfirmationScreenViewModel(authenticationService: AuthenticationService.mock, + authenticationFlow: flow, + slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL, + userIndicatorController: UserIndicatorControllerMock()) + } } diff --git a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/MockServerSelectionScreenState.swift b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/MockServerSelectionScreenState.swift index d97a7d37ea..c8393a4bdb 100644 --- a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/MockServerSelectionScreenState.swift +++ b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/MockServerSelectionScreenState.swift @@ -11,30 +11,22 @@ enum MockServerSelectionScreenState: CaseIterable { case matrix case emptyAddress case invalidAddress - case nonModal /// Generate the view struct for the screen state. @MainActor var viewModel: ServerSelectionScreenViewModel { switch self { case .matrix: return ServerSelectionScreenViewModel(homeserverAddress: "https://matrix.org", - slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL, - isModallyPresented: true) + slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL) case .emptyAddress: return ServerSelectionScreenViewModel(homeserverAddress: "", - slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL, - isModallyPresented: true) + slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL) case .invalidAddress: let viewModel = ServerSelectionScreenViewModel(homeserverAddress: "thisisbad", - slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL, - isModallyPresented: true) + slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL) viewModel.displayError(.footerMessage(L10n.errorUnknown)) return viewModel - case .nonModal: - return ServerSelectionScreenViewModel(homeserverAddress: "https://matrix.org", - slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL, - isModallyPresented: false) } } } diff --git a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenCoordinator.swift b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenCoordinator.swift index 6459f24a9e..71e78682e1 100644 --- a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenCoordinator.swift @@ -11,9 +11,9 @@ import SwiftUI struct ServerSelectionScreenCoordinatorParameters { /// The service used to authenticate the user. let authenticationService: AuthenticationServiceProtocol + let authenticationFlow: AuthenticationFlow + let slidingSyncLearnMoreURL: URL let userIndicatorController: UserIndicatorControllerProtocol - /// Whether the screen is presented modally or within a navigation stack. - let isModallyPresented: Bool } enum ServerSelectionScreenCoordinatorAction { @@ -38,8 +38,7 @@ final class ServerSelectionScreenCoordinator: CoordinatorProtocol { init(parameters: ServerSelectionScreenCoordinatorParameters) { self.parameters = parameters viewModel = ServerSelectionScreenViewModel(homeserverAddress: parameters.authenticationService.homeserver.value.address, - slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL, - isModallyPresented: parameters.isModallyPresented) + slidingSyncLearnMoreURL: parameters.slidingSyncLearnMoreURL) userIndicatorController = parameters.userIndicatorController } @@ -85,7 +84,7 @@ final class ServerSelectionScreenCoordinator: CoordinatorProtocol { startLoading() Task { - switch await authenticationService.configure(for: homeserverAddress) { + switch await authenticationService.configure(for: homeserverAddress, flow: parameters.authenticationFlow) { case .success: MXLog.info("Selected homeserver: \(homeserverAddress)") actionsSubject.send(.updated) @@ -107,6 +106,8 @@ final class ServerSelectionScreenCoordinator: CoordinatorProtocol { viewModel.displayError(.invalidWellKnownAlert(error)) case .slidingSyncNotAvailable: viewModel.displayError(.slidingSyncAlert) + case .registrationNotSupported: + viewModel.displayError(.registrationAlert) // TODO: [DOUG] Test me! default: viewModel.displayError(.footerMessage(L10n.errorUnknown)) } diff --git a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenModels.swift b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenModels.swift index 94eb3b09d8..ae11444bc8 100644 --- a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenModels.swift +++ b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenModels.swift @@ -22,19 +22,12 @@ struct ServerSelectionScreenViewState: BindableState { var bindings: ServerSelectionScreenBindings /// An error message to be shown in the text field footer. var footerErrorMessage: String? - /// Whether the screen is presented modally or within a navigation stack. - var isModallyPresented: Bool /// The message to show in the text field footer. var footerMessage: AttributedString { footerErrorMessage.map(AttributedString.init) ?? regularFooterMessage } - /// The title shown on the confirm button. - var buttonTitle: String { - isModallyPresented ? L10n.actionContinue : L10n.actionNext - } - /// The text field is showing an error. var isShowingFooterError: Bool { footerErrorMessage != nil @@ -45,10 +38,9 @@ struct ServerSelectionScreenViewState: BindableState { bindings.homeserverAddress.isEmpty || isShowingFooterError } - init(slidingSyncLearnMoreURL: URL, bindings: ServerSelectionScreenBindings, footerErrorMessage: String? = nil, isModallyPresented: Bool) { + init(slidingSyncLearnMoreURL: URL, bindings: ServerSelectionScreenBindings, footerErrorMessage: String? = nil) { self.bindings = bindings self.footerErrorMessage = footerErrorMessage - self.isModallyPresented = isModallyPresented let linkPlaceholder = "{link}" var message = AttributedString(L10n.screenChangeServerFormNotice(linkPlaceholder)) @@ -82,4 +74,6 @@ enum ServerSelectionScreenErrorType: Hashable { case invalidWellKnownAlert(String) /// An alert that allows the user to learn about sliding sync. case slidingSyncAlert + /// An alert that informs the user that registration isn't supported. + case registrationAlert } diff --git a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenViewModel.swift b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenViewModel.swift index 57d12c3dbb..fea0915420 100644 --- a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenViewModel.swift +++ b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenViewModel.swift @@ -19,13 +19,12 @@ class ServerSelectionScreenViewModel: ServerSelectionScreenViewModelType, Server actionsSubject.eraseToAnyPublisher() } - init(homeserverAddress: String, slidingSyncLearnMoreURL: URL, isModallyPresented: Bool) { + init(homeserverAddress: String, slidingSyncLearnMoreURL: URL) { self.slidingSyncLearnMoreURL = slidingSyncLearnMoreURL let bindings = ServerSelectionScreenBindings(homeserverAddress: homeserverAddress) super.init(initialViewState: ServerSelectionScreenViewState(slidingSyncLearnMoreURL: slidingSyncLearnMoreURL, - bindings: bindings, - isModallyPresented: isModallyPresented)) + bindings: bindings)) } override func process(viewAction: ServerSelectionScreenViewAction) { @@ -46,7 +45,7 @@ class ServerSelectionScreenViewModel: ServerSelectionScreenViewModelType, Server state.footerErrorMessage = message } case .invalidWellKnownAlert(let error): - state.bindings.alertInfo = AlertInfo(id: .slidingSyncAlert, + state.bindings.alertInfo = AlertInfo(id: .invalidWellKnownAlert(error), title: L10n.commonServerNotSupported, message: L10n.screenChangeServerErrorInvalidWellKnown(error)) case .slidingSyncAlert: @@ -56,6 +55,10 @@ class ServerSelectionScreenViewModel: ServerSelectionScreenViewModelType, Server message: L10n.screenChangeServerErrorNoSlidingSyncMessage, primaryButton: .init(title: L10n.actionLearnMore, role: .cancel, action: openURL), secondaryButton: .init(title: L10n.actionCancel, action: nil)) + case .registrationAlert: + state.bindings.alertInfo = AlertInfo(id: .registrationAlert, + title: L10n.errorUnknown, + message: L10n.errorAccountCreationNotPossible) } } diff --git a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/View/ServerSelectionScreen.swift b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/View/ServerSelectionScreen.swift index b166aabc67..03bb915504 100644 --- a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/View/ServerSelectionScreen.swift +++ b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/View/ServerSelectionScreen.swift @@ -64,7 +64,7 @@ struct ServerSelectionScreen: View { .onSubmit(submit) Button(action: submit) { - Text(context.viewState.buttonTitle) + Text(L10n.actionContinue) } .buttonStyle(.compound(.primary)) .disabled(context.viewState.hasValidationError) @@ -72,15 +72,12 @@ struct ServerSelectionScreen: View { } } - @ToolbarContentBuilder var toolbar: some ToolbarContent { ToolbarItem(placement: .cancellationAction) { - if context.viewState.isModallyPresented { - Button { context.send(viewAction: .dismiss) } label: { - Text(L10n.actionCancel) - } - .accessibilityIdentifier(A11yIdentifiers.changeServerScreen.dismiss) + Button { context.send(viewAction: .dismiss) } label: { + Text(L10n.actionCancel) } + .accessibilityIdentifier(A11yIdentifiers.changeServerScreen.dismiss) } } diff --git a/ElementX/Sources/Services/Authentication/AuthenticationClientBuilder.swift b/ElementX/Sources/Services/Authentication/AuthenticationClientBuilder.swift index 350e388246..dee843b5f5 100644 --- a/ElementX/Sources/Services/Authentication/AuthenticationClientBuilder.swift +++ b/ElementX/Sources/Services/Authentication/AuthenticationClientBuilder.swift @@ -8,8 +8,16 @@ import Foundation import MatrixRustSDK +// sourcery: AutoMockable +protocol AuthenticationClientBuilderProtocol { + func build(homeserverAddress: String) async throws -> ClientProtocol + func buildWithQRCode(qrCodeData: QrCodeData, + oidcConfiguration: OIDCConfigurationProxy, + progressListener: QrLoginProgressListenerProxy) async throws -> ClientProtocol +} + /// A wrapper around `ClientBuilder` to share reusable code between Normal and QR logins. -struct AuthenticationClientBuilder { +struct AuthenticationClientBuilder: AuthenticationClientBuilderProtocol { let sessionDirectories: SessionDirectories let passphrase: String let clientSessionDelegate: ClientSessionDelegate @@ -18,7 +26,7 @@ struct AuthenticationClientBuilder { let appHooks: AppHooks /// Builds a Client for login using OIDC or password authentication. - func build(homeserverAddress: String) async throws -> Client { + func build(homeserverAddress: String) async throws -> ClientProtocol { if appSettings.slidingSyncDiscovery == .forceNative { return try await makeClientBuilder(slidingSync: .forceNative).serverNameOrHomeserverUrl(serverNameOrUrl: homeserverAddress).build() } @@ -38,7 +46,7 @@ struct AuthenticationClientBuilder { /// Builds a Client, authenticating with the given QR code data. func buildWithQRCode(qrCodeData: QrCodeData, oidcConfiguration: OIDCConfigurationProxy, - progressListener: QrLoginProgressListenerProxy) async throws -> Client { + progressListener: QrLoginProgressListenerProxy) async throws -> ClientProtocol { if appSettings.slidingSyncDiscovery == .forceNative { return try await makeClientBuilder(slidingSync: .forceNative).buildWithQrCode(qrCodeData: qrCodeData, oidcConfiguration: oidcConfiguration.rustValue, diff --git a/ElementX/Sources/Services/Authentication/AuthenticationClientBuilderFactory.swift b/ElementX/Sources/Services/Authentication/AuthenticationClientBuilderFactory.swift new file mode 100644 index 0000000000..873a17dfd9 --- /dev/null +++ b/ElementX/Sources/Services/Authentication/AuthenticationClientBuilderFactory.swift @@ -0,0 +1,33 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Foundation +import MatrixRustSDK + +// sourcery: AutoMockable +protocol AuthenticationClientBuilderFactoryProtocol { + func makeBuilder(sessionDirectories: SessionDirectories, + passphrase: String, + clientSessionDelegate: ClientSessionDelegate, + appSettings: AppSettings, + appHooks: AppHooks) -> AuthenticationClientBuilderProtocol +} + +/// A wrapper around `ClientBuilder` to share reusable code between Normal and QR logins. +struct AuthenticationClientBuilderFactory: AuthenticationClientBuilderFactoryProtocol { + func makeBuilder(sessionDirectories: SessionDirectories, + passphrase: String, + clientSessionDelegate: ClientSessionDelegate, + appSettings: AppSettings, + appHooks: AppHooks) -> AuthenticationClientBuilderProtocol { + AuthenticationClientBuilder(sessionDirectories: sessionDirectories, + passphrase: passphrase, + clientSessionDelegate: clientSessionDelegate, + appSettings: appSettings, + appHooks: appHooks) + } +} diff --git a/ElementX/Sources/Services/Authentication/AuthenticationService.swift b/ElementX/Sources/Services/Authentication/AuthenticationService.swift index c799a8f313..66dbacb2ae 100644 --- a/ElementX/Sources/Services/Authentication/AuthenticationService.swift +++ b/ElementX/Sources/Services/Authentication/AuthenticationService.swift @@ -10,31 +10,39 @@ import Foundation import MatrixRustSDK class AuthenticationService: AuthenticationServiceProtocol { - private var client: Client? + private var client: ClientProtocol? private var sessionDirectories: SessionDirectories private let passphrase: String + private let clientBuilderFactory: AuthenticationClientBuilderFactoryProtocol private let userSessionStore: UserSessionStoreProtocol private let appSettings: AppSettings private let appHooks: AppHooks private let homeserverSubject: CurrentValueSubject var homeserver: CurrentValuePublisher { homeserverSubject.asCurrentValuePublisher() } + private(set) var flow: AuthenticationFlow - init(userSessionStore: UserSessionStoreProtocol, encryptionKeyProvider: EncryptionKeyProviderProtocol, appSettings: AppSettings, appHooks: AppHooks) { + init(userSessionStore: UserSessionStoreProtocol, + encryptionKeyProvider: EncryptionKeyProviderProtocol, + clientBuilderFactory: AuthenticationClientBuilderFactoryProtocol = AuthenticationClientBuilderFactory(), + appSettings: AppSettings, + appHooks: AppHooks) { sessionDirectories = .init() passphrase = encryptionKeyProvider.generateKey().base64EncodedString() + self.clientBuilderFactory = clientBuilderFactory self.userSessionStore = userSessionStore self.appSettings = appSettings self.appHooks = appHooks - homeserverSubject = .init(LoginHomeserver(address: appSettings.defaultHomeserverAddress, - loginMode: .unknown)) + // When updating these, don't forget to update the reset method too. + homeserverSubject = .init(LoginHomeserver(address: appSettings.defaultHomeserverAddress, loginMode: .unknown)) + flow = .login } // MARK: - Public - func configure(for homeserverAddress: String) async -> Result { + func configure(for homeserverAddress: String, flow: AuthenticationFlow) async -> Result { do { var homeserver = LoginHomeserver(address: homeserverAddress, loginMode: .unknown) @@ -57,7 +65,12 @@ class AuthenticationService: AuthenticationServiceProtocol { case .failure: nil } + if flow == .register, !homeserver.supportsRegistration { + return .failure(.registrationNotSupported) + } + self.client = client + self.flow = flow homeserverSubject.send(homeserver) return .success(()) } catch ClientBuildError.WellKnownDeserializationError(let error) { @@ -150,18 +163,24 @@ class AuthenticationService: AuthenticationServiceProtocol { } } + func reset() { + homeserverSubject.send(LoginHomeserver(address: appSettings.defaultHomeserverAddress, loginMode: .unknown)) + flow = .login + client = nil + } + // MARK: - Private - private func makeClientBuilder() -> AuthenticationClientBuilder { + private func makeClientBuilder() -> AuthenticationClientBuilderProtocol { // Use a fresh session directory each time the user enters a different server // so that caches (e.g. server versions) are always fresh for the new server. rotateSessionDirectory() - return AuthenticationClientBuilder(sessionDirectories: sessionDirectories, - passphrase: passphrase, - clientSessionDelegate: userSessionStore.clientSessionDelegate, - appSettings: appSettings, - appHooks: appHooks) + return clientBuilderFactory.makeBuilder(sessionDirectories: sessionDirectories, + passphrase: passphrase, + clientSessionDelegate: userSessionStore.clientSessionDelegate, + appSettings: appSettings, + appHooks: appHooks) } private func rotateSessionDirectory() { @@ -169,7 +188,7 @@ class AuthenticationService: AuthenticationServiceProtocol { sessionDirectories = .init() } - private func userSession(for client: Client) async -> Result { + private func userSession(for client: ClientProtocol) async -> Result { switch await userSessionStore.userSession(for: client, sessionDirectories: sessionDirectories, passphrase: passphrase) { case .success(let clientProxy): return .success(clientProxy) @@ -178,3 +197,13 @@ class AuthenticationService: AuthenticationServiceProtocol { } } } + +// MARK: - Mocks + +extension AuthenticationService { + static var mock = AuthenticationService(userSessionStore: UserSessionStoreMock(configuration: .init()), + encryptionKeyProvider: EncryptionKeyProvider(), + clientBuilderFactory: AuthenticationClientBuilderFactoryMock(configuration: .init()), + appSettings: ServiceLocator.shared.settings, + appHooks: AppHooks()) +} diff --git a/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift b/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift index e347bc7b98..3efa258e90 100644 --- a/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift +++ b/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift @@ -16,7 +16,7 @@ enum AuthenticationFlow { case register } -enum AuthenticationServiceError: Error { +enum AuthenticationServiceError: Error, Equatable { /// An error occurred during OIDC authentication. case oidcError(OIDCError) case invalidServer @@ -24,6 +24,7 @@ enum AuthenticationServiceError: Error { case invalidHomeserverAddress case invalidWellKnown(String) case slidingSyncNotAvailable + case registrationNotSupported case accountDeactivated case failedLoggingIn case sessionTokenRefreshNotSupported @@ -33,9 +34,11 @@ enum AuthenticationServiceError: Error { protocol AuthenticationServiceProtocol { /// The currently configured homeserver. var homeserver: CurrentValuePublisher { get } + /// The type of flow the service is currently configured with. + var flow: AuthenticationFlow { get } /// Sets up the service for login on the specified homeserver address. - func configure(for homeserverAddress: String) async -> Result + func configure(for homeserverAddress: String, flow: AuthenticationFlow) async -> Result /// Performs login using OIDC for the current homeserver. func urlForOIDCLogin() async -> Result /// Asks the SDK to abort an ongoing OIDC login if we didn't get a callback to complete the request with. @@ -46,6 +49,9 @@ protocol AuthenticationServiceProtocol { func login(username: String, password: String, initialDeviceName: String?, deviceID: String?) async -> Result /// Completes registration using the credentials obtained via the helper URL. func completeWebRegistration(using credentials: WebRegistrationCredentials) async -> Result + + /// Resets the current configuration requiring `configure(for:flow:)` to be called again. + func reset() } // MARK: - OIDC diff --git a/ElementX/Sources/Services/Authentication/MockAuthenticationService.swift b/ElementX/Sources/Services/Authentication/MockAuthenticationService.swift deleted file mode 100644 index 3cd72631ae..0000000000 --- a/ElementX/Sources/Services/Authentication/MockAuthenticationService.swift +++ /dev/null @@ -1,65 +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 Combine -import Foundation -import MatrixRustSDK - -class MockAuthenticationService: AuthenticationServiceProtocol { - let validCredentials = (username: "alice", password: "12345678") - - private let homeserverSubject: CurrentValueSubject - var homeserver: CurrentValuePublisher { homeserverSubject.asCurrentValuePublisher() } - - init(homeserver: LoginHomeserver = .mockMatrixDotOrg) { - homeserverSubject = .init(homeserver) - } - - func configure(for homeserverAddress: String) async -> Result { - // Map the address to the mock homeservers - if LoginHomeserver.mockMatrixDotOrg.address.contains(homeserverAddress) { - homeserverSubject.send(.mockMatrixDotOrg) - return .success(()) - } else if LoginHomeserver.mockOIDC.address.contains(homeserverAddress) { - homeserverSubject.send(.mockOIDC) - return .success(()) - } else if LoginHomeserver.mockBasicServer.address.contains(homeserverAddress) { - homeserverSubject.send(.mockBasicServer) - return .success(()) - } else if LoginHomeserver.mockUnsupported.address.contains(homeserverAddress) { - homeserverSubject.send(.mockUnsupported) - return .success(()) - } else { - // Otherwise fail with an invalid server. - return .failure(.invalidServer) - } - } - - func urlForOIDCLogin() async -> Result { - .failure(.oidcError(.notSupported)) - } - - func abortOIDCLogin(data: OIDCAuthorizationDataProxy) async { } - - func loginWithOIDCCallback(_ callbackURL: URL, data: OIDCAuthorizationDataProxy) async -> Result { - .failure(.oidcError(.notSupported)) - } - - func login(username: String, password: String, initialDeviceName: String?, deviceID: String?) async -> Result { - // Login only succeeds if the username and password match the valid credentials property - guard username == validCredentials.username, password == validCredentials.password else { - return .failure(.invalidCredentials) - } - - let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: username)))) - return .success(userSession) - } - - func completeWebRegistration(using credentials: WebRegistrationCredentials) async -> Result { - .failure(.failedLoggingIn) - } -} diff --git a/ElementX/Sources/Services/QRCode/QRCodeLoginService.swift b/ElementX/Sources/Services/QRCode/QRCodeLoginService.swift index 015a6a9d09..d1f4eaad2f 100644 --- a/ElementX/Sources/Services/QRCode/QRCodeLoginService.swift +++ b/ElementX/Sources/Services/QRCode/QRCodeLoginService.swift @@ -81,7 +81,7 @@ final class QRCodeLoginService: QRCodeLoginServiceProtocol { sessionDirectories = .init() } - private func userSession(for client: Client) async -> Result { + private func userSession(for client: ClientProtocol) async -> Result { switch await userSessionStore.userSession(for: client, sessionDirectories: sessionDirectories, passphrase: passphrase) { case .success(let session): return .success(session) diff --git a/ElementX/Sources/Services/UserSession/UserSessionStore.swift b/ElementX/Sources/Services/UserSession/UserSessionStore.swift index 6b7f94908c..ab38d90a9c 100644 --- a/ElementX/Sources/Services/UserSession/UserSessionStore.swift +++ b/ElementX/Sources/Services/UserSession/UserSessionStore.swift @@ -60,7 +60,7 @@ class UserSessionStore: UserSessionStoreProtocol { } } - func userSession(for client: Client, sessionDirectories: SessionDirectories, passphrase: String?) async -> Result { + func userSession(for client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String?) async -> Result { do { let session = try client.session() let userID = try client.userId() @@ -146,7 +146,7 @@ class UserSessionStore: UserSessionStoreProtocol { } } - private func setupProxyForClient(_ client: Client) async -> ClientProxyProtocol { + private func setupProxyForClient(_ client: ClientProtocol) async -> ClientProxyProtocol { await ClientProxy(client: client, networkMonitor: networkMonitor, appSettings: appSettings) diff --git a/ElementX/Sources/Services/UserSession/UserSessionStoreProtocol.swift b/ElementX/Sources/Services/UserSession/UserSessionStoreProtocol.swift index fd841ae699..64772c3672 100644 --- a/ElementX/Sources/Services/UserSession/UserSessionStoreProtocol.swift +++ b/ElementX/Sources/Services/UserSession/UserSessionStoreProtocol.swift @@ -14,6 +14,7 @@ enum UserSessionStoreError: Error { case failedSettingUpSession } +// sourcery: AutoMockable protocol UserSessionStoreProtocol { /// Deletes all data stored in the shared container and keychain func reset() @@ -31,7 +32,7 @@ protocol UserSessionStoreProtocol { func restoreUserSession() async -> Result /// Creates a user session for a new client from the SDK along with the passphrase used for the data stores. - func userSession(for client: Client, sessionDirectories: SessionDirectories, passphrase: String?) async -> Result + func userSession(for client: ClientProtocol, sessionDirectories: SessionDirectories, passphrase: String?) async -> Result /// Logs out of the specified session. func logout(userSession: UserSessionProtocol) diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index ac29d717a4..2af7f39bcc 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -109,22 +109,16 @@ class MockScreen: Identifiable { lazy var coordinator: CoordinatorProtocol? = { switch id { - case .login: - let navigationStackCoordinator = NavigationStackCoordinator() - let coordinator = LoginScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationService(), - analytics: ServiceLocator.shared.analytics, - userIndicatorController: ServiceLocator.shared.userIndicatorController)) - navigationStackCoordinator.setRootCoordinator(coordinator) - return navigationStackCoordinator case .serverSelection: let navigationStackCoordinator = NavigationStackCoordinator() - let coordinator = ServerSelectionScreenCoordinator(parameters: .init(authenticationService: MockAuthenticationService(), - userIndicatorController: ServiceLocator.shared.userIndicatorController, - isModallyPresented: true)) + let coordinator = ServerSelectionScreenCoordinator(parameters: .init(authenticationService: AuthenticationService.mock, + authenticationFlow: .login, + slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL, + userIndicatorController: ServiceLocator.shared.userIndicatorController)) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator case .authenticationFlow: - let flowCoordinator = AuthenticationFlowCoordinator(authenticationService: MockAuthenticationService(), + let flowCoordinator = AuthenticationFlowCoordinator(authenticationService: AuthenticationService.mock, qrCodeLoginService: QRCodeLoginServiceMock(), bugReportService: BugReportServiceMock(), navigationRootCoordinator: navigationRootCoordinator, diff --git a/ElementX/Sources/UITests/UITestsScreenIdentifier.swift b/ElementX/Sources/UITests/UITestsScreenIdentifier.swift index 043b0b3778..b725b2060a 100644 --- a/ElementX/Sources/UITests/UITestsScreenIdentifier.swift +++ b/ElementX/Sources/UITests/UITestsScreenIdentifier.swift @@ -20,7 +20,6 @@ enum UITestsScreenIdentifier: String { case createPoll case createRoom case createRoomNoUsers - case login case roomLayoutBottom case roomLayoutMiddle case roomLayoutTop diff --git a/UITests/Sources/AuthenticationFlowCoordinatorUITests.swift b/UITests/Sources/AuthenticationFlowCoordinatorUITests.swift index e65b8c7875..0e38601c4b 100644 --- a/UITests/Sources/AuthenticationFlowCoordinatorUITests.swift +++ b/UITests/Sources/AuthenticationFlowCoordinatorUITests.swift @@ -19,6 +19,10 @@ class AuthenticationFlowCoordinatorUITests: XCTestCase { // Server Confirmation: Tap continue button app.buttons[A11yIdentifiers.serverConfirmationScreen.continue].tap() + // Login Screen: Wait for continue button to appear + let continueButton = app.buttons[A11yIdentifiers.loginScreen.continue] + XCTAssertTrue(continueButton.waitForExistence(timeout: 2.0)) + // Login Screen: Enter valid credentials app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("alice\n") app.secureTextFields[A11yIdentifiers.loginScreen.password].clearAndTypeText("12345678") @@ -39,20 +43,43 @@ class AuthenticationFlowCoordinatorUITests: XCTestCase { // Server Confirmation: Tap continue button app.buttons[A11yIdentifiers.serverConfirmationScreen.continue].tap() + // Login Screen: Wait for continue button to appear + let continueButton = app.buttons[A11yIdentifiers.loginScreen.continue] + XCTAssertTrue(continueButton.waitForExistence(timeout: 2.0)) + // Login Screen: Enter invalid credentials app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("alice") app.secureTextFields[A11yIdentifiers.loginScreen.password].clearAndTypeText("87654321") - // Login Screen: Tap next - let nextButton = app.buttons[A11yIdentifiers.loginScreen.continue] - XCTAssertTrue(nextButton.waitForExistence(timeout: 2.0)) - XCTAssertTrue(nextButton.isEnabled) - nextButton.tap() + // Login Screen: Tap continue + XCTAssertTrue(continueButton.isEnabled) + continueButton.tap() // Then login should fail. XCTAssertTrue(app.alerts.element.waitForExistence(timeout: 2.0), "An error alert should be shown when attempting login with invalid credentials.") } + func testLoginWithUnsupportedUserID() async throws { + // Given the authentication flow. + let app = Application.launch(.authenticationFlow) + + // Splash Screen: Tap get started button + app.buttons[A11yIdentifiers.authenticationStartScreen.signIn].tap() + + // Server Confirmation: Tap continue button + app.buttons[A11yIdentifiers.serverConfirmationScreen.continue].tap() + + // Login Screen: Wait for continue button to appear + let continueButton = app.buttons[A11yIdentifiers.loginScreen.continue] + XCTAssertTrue(continueButton.waitForExistence(timeout: 2.0)) + + // When entering a username on a homeserver with an unsupported flow. + app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("@test:server.net\n") + + // Then the screen should not allow login to continue. + try await app.assertScreenshot(.authenticationFlow, step: 1) + } + func testSelectingOIDCServer() { // Given the authentication flow. let app = Application.launch(.authenticationFlow) diff --git a/UITests/Sources/LoginScreenUITests.swift b/UITests/Sources/LoginScreenUITests.swift deleted file mode 100644 index 2275e1648a..0000000000 --- a/UITests/Sources/LoginScreenUITests.swift +++ /dev/null @@ -1,35 +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 XCTest - -@MainActor -class LoginScreenUITests: XCTestCase { - func testMatrixDotOrg() async throws { - // Given the initial login screen which defaults to matrix.org. - let app = Application.launch(.login) - try await app.assertScreenshot(.login) - - // When typing in a username and password. - app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("@test:matrix.org") - app.secureTextFields[A11yIdentifiers.loginScreen.password].clearAndTypeText("12345678") - - // Then the form should be ready to submit. - try await app.assertScreenshot(.login, step: 0) - } - - func testUnsupported() async throws { - // Given the initial login screen. - let app = Application.launch(.login) - - // When entering a username on a homeserver with an unsupported flow. - app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("@test:server.net\n") - - // Then the screen should not allow login to continue. - try await app.assertScreenshot(.login, step: 1) - } -} diff --git a/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPad-10th-generation-en-GB.UI.png new file mode 100644 index 0000000000..c65a1a76d9 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPad-10th-generation-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b059e871878224c54b4b69d59f95394cf73c5d86e0f83ea030808efb9b48767 +size 88666 diff --git a/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPhone-15-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPhone-15-en-GB.UI.png new file mode 100644 index 0000000000..5de379a22c --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPhone-15-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58bcbecf677ba5b1ce9fc86a541e1df0389464677b882d34903be09512c65f46 +size 94767 diff --git a/UITests/Sources/__Snapshots__/Application/login-0-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/login-0-iPad-10th-generation-en-GB.UI.png deleted file mode 100644 index 7bde1d70de..0000000000 --- a/UITests/Sources/__Snapshots__/Application/login-0-iPad-10th-generation-en-GB.UI.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bbe00c86de31bcf15f2c80631a11ea4ff89a4b6d220209f18c11499750472e44 -size 86311 diff --git a/UITests/Sources/__Snapshots__/Application/login-0-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/login-0-iPhone-16-en-GB.UI.png deleted file mode 100644 index a08e21ecba..0000000000 --- a/UITests/Sources/__Snapshots__/Application/login-0-iPhone-16-en-GB.UI.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ac640dca1b9e754b7ecdb958568473270838728f7dcf2d6a638b3495541f4420 -size 89495 diff --git a/UITests/Sources/__Snapshots__/Application/login-1-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/login-1-iPad-10th-generation-en-GB.UI.png deleted file mode 100644 index 9544d2d882..0000000000 --- a/UITests/Sources/__Snapshots__/Application/login-1-iPad-10th-generation-en-GB.UI.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4f14c2216af345d3f620642f344f60fc79e877e78e651b33e20c3ebc0e8ef752 -size 85957 diff --git a/UITests/Sources/__Snapshots__/Application/login-1-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/login-1-iPhone-16-en-GB.UI.png deleted file mode 100644 index 4e9a7119dd..0000000000 --- a/UITests/Sources/__Snapshots__/Application/login-1-iPhone-16-en-GB.UI.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b22d205a8a1f5d5889271a7fa93678112bebc8c8e73e21eaa8722d1e98bd427e -size 89727 diff --git a/UITests/Sources/__Snapshots__/Application/login-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/login-iPad-10th-generation-en-GB.UI.png deleted file mode 100644 index dcc8187fec..0000000000 --- a/UITests/Sources/__Snapshots__/Application/login-iPad-10th-generation-en-GB.UI.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:58e3224bd82abb9548d042e02a081ca5a73fd4685c2769d46f39d14da5769ace -size 84034 diff --git a/UITests/Sources/__Snapshots__/Application/login-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/login-iPhone-16-en-GB.UI.png deleted file mode 100644 index 491ac73e17..0000000000 --- a/UITests/Sources/__Snapshots__/Application/login-iPhone-16-en-GB.UI.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c626719a64e6f68e087f616f43ab6890a2c0be081c66b0bc51f6b308e1849197 -size 86066 diff --git a/UnitTests/Sources/AuthenticationServiceTests.swift b/UnitTests/Sources/AuthenticationServiceTests.swift new file mode 100644 index 0000000000..e512db7318 --- /dev/null +++ b/UnitTests/Sources/AuthenticationServiceTests.swift @@ -0,0 +1,96 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import XCTest + +@testable import ElementX + +class AuthenticationServiceTests: XCTestCase { + var client: ClientSDKMock! + var userSessionStore: UserSessionStoreMock! + var encryptionKeyProvider: MockEncryptionKeyProvider! + + var service: AuthenticationService! + + func testLogin() async { + setupMocks() + + switch await service.configure(for: "matrix.org", flow: .login) { + case .success: + break + case .failure(let error): + XCTFail("Unexpected failure: \(error)") + } + + XCTAssertEqual(service.flow, .login) + XCTAssertEqual(service.homeserver.value, .mockMatrixDotOrg) + + switch await service.login(username: "alice", password: "12345678", initialDeviceName: nil, deviceID: nil) { + case .success: + XCTAssertEqual(client.loginUsernamePasswordInitialDeviceNameDeviceIdCallsCount, 1) + XCTAssertEqual(userSessionStore.userSessionForSessionDirectoriesPassphraseCallsCount, 1) + XCTAssertEqual(userSessionStore.userSessionForSessionDirectoriesPassphraseReceivedArguments?.passphrase, + encryptionKeyProvider.generateKey().base64EncodedString()) + case .failure(let error): + XCTFail("Unexpected failure: \(error)") + } + } + + func testConfigureRegister() async { + setupMocks() + + switch await service.configure(for: "matrix.org", flow: .register) { + case .success: + break + case .failure(let error): + XCTFail("Unexpected failure: \(error)") + } + + XCTAssertEqual(service.flow, .register) + XCTAssertEqual(service.homeserver.value, .mockMatrixDotOrg) + } + + func testConfigureRegisterNoSupport() async { + let homeserverAddress = "example.com" + setupMocks(serverAddress: homeserverAddress) + + switch await service.configure(for: homeserverAddress, flow: .register) { + case .success: + XCTFail("Configuration should have failed") + case .failure(let error): + XCTAssertEqual(error, .registrationNotSupported) + } + + XCTAssertEqual(service.flow, .login) + XCTAssertEqual(service.homeserver.value, .init(address: "matrix.org", loginMode: .unknown)) + } + + // MARK: - Helpers + + private func setupMocks(serverAddress: String = "matrix.org") { + let configuration: AuthenticationClientBuilderMock.Configuration = .init() + let clientBuilderFactory = AuthenticationClientBuilderFactoryMock(configuration: .init(builderConfiguration: configuration)) + + client = configuration.homeserverClients[serverAddress] + userSessionStore = UserSessionStoreMock(configuration: .init()) + encryptionKeyProvider = MockEncryptionKeyProvider() + + service = AuthenticationService(userSessionStore: userSessionStore, + encryptionKeyProvider: encryptionKeyProvider, + clientBuilderFactory: clientBuilderFactory, + appSettings: ServiceLocator.shared.settings, + appHooks: AppHooks()) + } +} + +struct MockEncryptionKeyProvider: EncryptionKeyProviderProtocol { + private let key = "12345678" + + func generateKey() -> Data { + Data(key.utf8) + } +} diff --git a/UnitTests/Sources/ServerConfigurationScreenViewStateTests.swift b/UnitTests/Sources/ServerConfigurationScreenViewStateTests.swift index b859e17414..d6a0428dae 100644 --- a/UnitTests/Sources/ServerConfigurationScreenViewStateTests.swift +++ b/UnitTests/Sources/ServerConfigurationScreenViewStateTests.swift @@ -26,18 +26,11 @@ class ServerConfirmationScreenViewStateTests: XCTestCase { func testRegisterMessageString() { let matrixDotOrgRegister = ServerConfirmationScreenViewState(homeserverAddress: LoginHomeserver.mockMatrixDotOrg.address, - authenticationFlow: .register, - homeserverSupportsRegistration: true) + authenticationFlow: .register) XCTAssertEqual(matrixDotOrgRegister.message, L10n.screenServerConfirmationMessageRegister, "The registration message should always be the same.") let oidcRegister = ServerConfirmationScreenViewState(homeserverAddress: LoginHomeserver.mockOIDC.address, - authenticationFlow: .register, - homeserverSupportsRegistration: true) + authenticationFlow: .register) XCTAssertEqual(oidcRegister.message, L10n.screenServerConfirmationMessageRegister, "The registration message should always be the same.") - - let otherRegister = ServerConfirmationScreenViewState(homeserverAddress: LoginHomeserver.mockBasicServer.address, - authenticationFlow: .register, - homeserverSupportsRegistration: false) - XCTAssertEqual(otherRegister.message, L10n.errorAccountCreationNotPossible, "The registration message should always be the same.") } } diff --git a/UnitTests/Sources/ServerConfirmationScreenViewModelTests.swift b/UnitTests/Sources/ServerConfirmationScreenViewModelTests.swift index 0d89417d09..9f9d3148d1 100644 --- a/UnitTests/Sources/ServerConfirmationScreenViewModelTests.swift +++ b/UnitTests/Sources/ServerConfirmationScreenViewModelTests.swift @@ -11,5 +11,117 @@ import XCTest @MainActor class ServerConfirmationScreenViewModelTests: XCTestCase { - // Nothing to test, the view model has no mutable state. + var clientBuilderFactory: AuthenticationClientBuilderFactoryMock! + var service: AuthenticationServiceProtocol! + + var viewModel: ServerConfirmationScreenViewModel! + var context: ServerConfirmationScreenViewModel.Context { viewModel.context } + + func testConfirmLoginWithoutConfiguration() async throws { + // Given a view model for login using a service that hasn't been configured. + setupViewModel(authenticationFlow: .login) + XCTAssertEqual(service.homeserver.value.loginMode, .unknown) + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0) + + // When continuing from the confirmation screen. + let deferred = deferFulfillment(viewModel.actions) { $0 == .confirm } + context.send(viewAction: .confirm) + try await deferred.fulfill() + + // Then a call to configure service should be made. + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1) + XCTAssertNotEqual(service.homeserver.value.loginMode, .unknown) + } + + func testConfirmLoginAfterConfiguration() async throws { + // Given a view model for login using a service that has already been configured (via the server selection screen). + setupViewModel(authenticationFlow: .login) + guard case .success = await service.configure(for: viewModel.state.homeserverAddress, flow: .login) else { + XCTFail("The configuration should succeed.") + return + } + XCTAssertNotEqual(service.homeserver.value.loginMode, .unknown) + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1) + + // When continuing from the confirmation screen. + let deferred = deferFulfillment(viewModel.actions) { $0 == .confirm } + context.send(viewAction: .confirm) + try await deferred.fulfill() + + // Then the configured homeserver should be used and no additional call should be made to the service. + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1) + } + + func testConfirmRegisterWithoutConfiguration() async throws { + // Given a view model for registration using a service that hasn't been configured. + setupViewModel(authenticationFlow: .register) + XCTAssertEqual(service.homeserver.value.loginMode, .unknown) + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0) + + // When continuing from the confirmation screen. + let deferred = deferFulfillment(viewModel.actions) { $0 == .confirm } + context.send(viewAction: .confirm) + try await deferred.fulfill() + + // Then a call to configure service should be made. + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1) + XCTAssertNotEqual(service.homeserver.value.loginMode, .unknown) + } + + func testConfirmRegisterAfterConfiguration() async throws { + // Given a view model for registration using a service that has already been configured (via the server selection screen). + setupViewModel(authenticationFlow: .register) + guard case .success = await service.configure(for: viewModel.state.homeserverAddress, flow: .register) else { + XCTFail("The configuration should succeed.") + return + } + XCTAssertNotEqual(service.homeserver.value.loginMode, .unknown) + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1) + + // When continuing from the confirmation screen. + let deferred = deferFulfillment(viewModel.actions) { $0 == .confirm } + context.send(viewAction: .confirm) + try await deferred.fulfill() + + // Then the configured homeserver should be used and no additional call should be made to the service. + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1) + } + + func testRegistrationNotSupportedAlert() async throws { + // Given a view model for registration using a service that hasn't been configured and the default server doesn't support registration. + setupViewModel(authenticationFlow: .register, elementWellKnown: false) + XCTAssertEqual(service.homeserver.value.loginMode, .unknown) + XCTAssertFalse(service.homeserver.value.supportsRegistration) + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0) + XCTAssertNil(context.alertInfo) + + // When continuing from the confirmation screen. + let deferred = deferFulfillment(context.$viewState) { $0.bindings.alertInfo != nil } + context.send(viewAction: .confirm) + try await deferred.fulfill() + + // Then the configured homeserver should be used and no additional call should be made to the service. + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1) + XCTAssertEqual(context.alertInfo?.id, .registration) + } + + // MARK: - Helpers + + private func setupViewModel(authenticationFlow: AuthenticationFlow, elementWellKnown: Bool = true) { + let client = ClientSDKMock(configuration: elementWellKnown ? .init() : .init(elementWellKnown: "")) + let configuration = AuthenticationClientBuilderMock.Configuration(homeserverClients: ["matrix.org": client], + qrCodeClient: client) + + clientBuilderFactory = AuthenticationClientBuilderFactoryMock(configuration: .init(builderConfiguration: configuration)) + service = AuthenticationService(userSessionStore: UserSessionStoreMock(configuration: .init()), + encryptionKeyProvider: EncryptionKeyProvider(), + clientBuilderFactory: clientBuilderFactory, + appSettings: ServiceLocator.shared.settings, + appHooks: AppHooks()) + + viewModel = ServerConfirmationScreenViewModel(authenticationService: service, + authenticationFlow: authenticationFlow, + slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL, + userIndicatorController: UserIndicatorControllerMock()) + } } diff --git a/UnitTests/Sources/ServerSelectionViewModelTests.swift b/UnitTests/Sources/ServerSelectionViewModelTests.swift index b6aafa1a6d..7808b62249 100644 --- a/UnitTests/Sources/ServerSelectionViewModelTests.swift +++ b/UnitTests/Sources/ServerSelectionViewModelTests.swift @@ -16,8 +16,7 @@ class ServerSelectionViewModelTests: XCTestCase { @MainActor override func setUp() { viewModel = ServerSelectionScreenViewModel(homeserverAddress: "", - slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL, - isModallyPresented: true) + slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL) context = viewModel.context } From dc6d7f2985f7cca105280faf8ee4e59ea41fca36 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Thu, 26 Sep 2024 10:15:09 +0300 Subject: [PATCH 006/114] Integration test improvements (#3330) * Send a message from integration tests and check that it doesn't show up in the app logs * Increase photo library timeout * Wait for image tapping to process * Send a message using the rich text editor too * Wait for the various pickers to finish loading * Set all logs to `trace` in integration tests * Close formatting options after sending the second message * Make sure logs are actually sent to tracing and get redirected to the right file * Switch from using a custom `trace` log configuration to the lowest level `TracingConfiguration` we support --- .github/workflows/integration-tests.yml | 6 ++ ElementX.xcodeproj/project.pbxproj | 12 ++- .../Other/AccessibilityIdentifiers.swift | 3 + .../Other/{ => Extensions}/ProcessInfo.swift | 0 .../Sources/Other/Logging/RustTracing.swift | 18 ++++- .../View/ComposerToolbar.swift | 2 + IntegrationTests/Sources/UserFlowTests.swift | 74 ++++++++++++++++--- NSE/SupportingFiles/target.yml | 2 + 8 files changed, 101 insertions(+), 16 deletions(-) rename ElementX/Sources/Other/{ => Extensions}/ProcessInfo.swift (100%) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 7b0eb99c0b..4b0661b05a 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -37,6 +37,12 @@ jobs: INTEGRATION_TESTS_USERNAME: ${{ secrets.INTEGRATION_TESTS_USERNAME }} INTEGRATION_TESTS_PASSWORD: ${{ secrets.INTEGRATION_TESTS_PASSWORD }} + - name: Check logs are set to the `trace` level + run: (grep ' TRACE ' /Users/Shared -qR) + + - name: Check logs don't contain private messages + run: (! grep 'Go down in flames' /Users/Shared -qR) + - name: Zip results # for faster upload if: failure() working-directory: fastlane/test_output diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 8aeb88c97b..008050994d 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -311,6 +311,7 @@ 454311EAC17D778E19F46592 /* NotificationPermissionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91868EB98818044E6FEBE532 /* NotificationPermissionsScreenCoordinator.swift */; }; 454F8DDC4442C0DE54094902 /* LABiometryType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3F219838588C62198E726E3 /* LABiometryType.swift */; }; 4557192F5B15A8D9BB920232 /* AdvancedSettingsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E492690C8B27A892C194CC4 /* AdvancedSettingsScreenCoordinator.swift */; }; + 45D6DC594816288983627484 /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; }; 46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */; }; 4681820102DAC8BA586357D4 /* VoiceMessageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D7926A5684E18196B538 /* VoiceMessageCache.swift */; }; 46A183C6125A669AEB005699 /* UserProfileScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F134D2D91DFF732FB75B2CB7 /* UserProfileScreenViewModelProtocol.swift */; }; @@ -458,6 +459,7 @@ 663E198678778F7426A9B27D /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FAFE1C2149E6AC8156ED2B /* Collection.swift */; }; 67160204A8D362BB7D4AD259 /* Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693E16574C6F7F9FA1015A8C /* Search.swift */; }; 6786C4B0936AC84D993B20BF /* NotificationSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F06F2F09B2EDD067DC2174 /* NotificationSettingsScreen.swift */; }; + 6793E75E3EBE48EBB8F857AF /* ProcessInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077B01C13BBA2996272C5FB5 /* ProcessInfo.swift */; }; 67C05C50AD734283374605E3 /* MatrixEntityRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */; }; 67D6E0700A9C1E676F6231F8 /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = AD544C0FA48DFFB080920061 /* Collections */; }; 67E9926C4572C54F59FCA91A /* AuthenticationFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9B069D7772DDF6513E0F1B8 /* AuthenticationFlowCoordinator.swift */; }; @@ -525,6 +527,7 @@ 7640A4B412CACF15D143CCD4 /* Strings+SAS.swift in Sources */ = {isa = PBXBuildFile; fileRef = B172057567E049007A5C4D92 /* Strings+SAS.swift */; }; 767D366C40F1311CFA333763 /* PillContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86376BEE425704AEE197CA54 /* PillContext.swift */; }; 7691233E3572A9173FD96CB3 /* SecureBackupKeyBackupScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E88534A39781D76487D59DF /* SecureBackupKeyBackupScreenViewModelTests.swift */; }; + 76C874243A8C440D6CF7B344 /* ProcessInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077B01C13BBA2996272C5FB5 /* ProcessInfo.swift */; }; 7708976CEE6AFB5CFAEFBA68 /* PillTextAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CF1EE0AA78470C674554262 /* PillTextAttachment.swift */; }; 7756C4E90CABE6F14F7920A0 /* BugReportUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6FEA87EA3752203065ECE27 /* BugReportUITests.swift */; }; 77574A519A4E484880053EAD /* IdentityConfirmationScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FDF541AE914059942B575B4 /* IdentityConfirmationScreenModels.swift */; }; @@ -1130,7 +1133,6 @@ FCDA202B246F75BA28E10C5F /* MapTilerAuthorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = E062C1750EFC8627DE4CAB8E /* MapTilerAuthorization.swift */; }; FD29471C72872F8B7580E3E1 /* KeychainControllerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39C0D861FC397AC34BCF089E /* KeychainControllerMock.swift */; }; FD4C21F8DA1E273DE94FCD1A /* NotificationItemProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B927CF5EF7FCCDA5EDC474B /* NotificationItemProxyProtocol.swift */; }; - FD4DEC88210F35C35B2FB386 /* ProcessInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A1398EFF65090FDA1CB639 /* ProcessInfo.swift */; }; FD762761C5D0C30E6255C3D8 /* ServerConfirmationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA4CF2F5B4F68D02E412004 /* ServerConfirmationScreenViewModelProtocol.swift */; }; FDD5B4B616D9FF4DE3E9A418 /* QRCodeScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92DB574F954CC2B40F7BE892 /* QRCodeScannerView.swift */; }; FE4593FC2A02AAF92E089565 /* ElementAnimations.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF1593DD87F974F8509BB619 /* ElementAnimations.swift */; }; @@ -1234,6 +1236,7 @@ 06FAE373A7F20780BA84B59C /* MessageForwardingScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreenCoordinator.swift; sourceTree = ""; }; 0724EBDFE8BB4C9E5547C57D /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = ""; }; 07579F9C29001E40715F3014 /* NotificationSettingsChatType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsChatType.swift; sourceTree = ""; }; + 077B01C13BBA2996272C5FB5 /* ProcessInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessInfo.swift; sourceTree = ""; }; 077D7C3BE199B6E5DDEC07EC /* AppCoordinatorStateMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinatorStateMachine.swift; sourceTree = ""; }; 07C6B0B087FE6601C3F77816 /* JoinedRoomProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinedRoomProxy.swift; sourceTree = ""; }; 0825EAFD47332DD459DE893F /* SessionDirectoriesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDirectoriesTests.swift; sourceTree = ""; }; @@ -1974,7 +1977,6 @@ B2EAFFD44F81F86012D6EC27 /* AudioRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRoomTimelineView.swift; sourceTree = ""; }; B3005886F00029F058DB62BE /* StartChatScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreenCoordinator.swift; sourceTree = ""; }; B383DCD3DCB19E00FD478A5F /* ConfirmationDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationDialog.swift; sourceTree = ""; }; - B3A1398EFF65090FDA1CB639 /* ProcessInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessInfo.swift; sourceTree = ""; }; B4005D82E9D27BAF006A8FE1 /* AppLockScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenViewModel.swift; sourceTree = ""; }; B40233F2989AD49906BB310D /* RoomPollsHistoryScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreenViewModelTests.swift; sourceTree = ""; }; B410B32B72C90BF94E481F33 /* AppLockSetupPINScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenModels.swift; sourceTree = ""; }; @@ -3260,6 +3262,7 @@ 95BAC0F6C9644336E9567EE6 /* NSRegularExpresion.swift */, 62B07B296D7A9D2F09120853 /* OrderedSet.swift */, D26813CCE39221FE30BF22CD /* PlatformViewVersionPredicate.swift */, + 077B01C13BBA2996272C5FB5 /* ProcessInfo.swift */, 1DFE0E493FB55E5A62E7852A /* ProposedViewSize.swift */, 7310D8DFE01AF45F0689C3AA /* Publisher.swift */, 584A61D9C459FAFEF038A7C0 /* Section.swift */, @@ -4866,7 +4869,6 @@ 7B25F959A434BB9923A3223F /* ExpiringTaskRunner.swift */, 6A580295A56B55A856CC4084 /* InfoPlistReader.swift */, 6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */, - B3A1398EFF65090FDA1CB639 /* ProcessInfo.swift */, 53482ECA4B6633961EC224F5 /* ScrollViewAdapter.swift */, 4481799F455B3DA243BDA2AC /* ShareToMapsAppActivity.swift */, B1E227F34BE43B08E098796E /* TestablePreview.swift */, @@ -6053,6 +6055,7 @@ 62418EA4E3EB597AD184AEB6 /* PillConstants.swift in Sources */, 55CDD3968D95D1A820B5491E /* PlaceholderAvatarImage.swift in Sources */, F12F6BED7B6D7EE4BEE55039 /* PlainMentionBuilder.swift in Sources */, + 76C874243A8C440D6CF7B344 /* ProcessInfo.swift in Sources */, 414F50CFCFEEE2611127DCFB /* RestorationToken.swift in Sources */, 17BC15DA08A52587466698C5 /* RoomMessageEventStringBuilder.swift in Sources */, 7354D094A4C59B555F407FA1 /* RustTracing.swift in Sources */, @@ -6063,6 +6066,7 @@ E0FB26262689F04D66A949D7 /* TestablePreview.swift in Sources */, 24DF253C18D3E2C56DD0E597 /* TracingConfiguration.swift in Sources */, DDB47D29C6865669288BF87C /* UIFont+AttributedStringBuilder.m in Sources */, + 45D6DC594816288983627484 /* UITestsScreenIdentifier.swift in Sources */, 281BED345D59A9A6A99E9D98 /* UNNotificationContent.swift in Sources */, 518C93DC6516D3D018DE065F /* UNNotificationRequest.swift in Sources */, 06B55882911B4BF5B14E9851 /* URL.swift in Sources */, @@ -6672,7 +6676,7 @@ 128FFD8A3D85845F9A927F47 /* PollRoomTimelineView.swift in Sources */, 1307268DC41730E5BCF7D9A0 /* PollView.swift in Sources */, DF504B10A4918F971A57BEF2 /* PostHogAnalyticsClient.swift in Sources */, - FD4DEC88210F35C35B2FB386 /* ProcessInfo.swift in Sources */, + 6793E75E3EBE48EBB8F857AF /* ProcessInfo.swift in Sources */, 69DE29C3E3180BB17D840690 /* ProgressCursorModifier.swift in Sources */, C7ABEBECDC513F7887DACF66 /* ProgressMaskModifier.swift in Sources */, 9B356742E035D90A8BB5CABE /* ProposedViewSize.swift in Sources */, diff --git a/ElementX/Sources/Other/AccessibilityIdentifiers.swift b/ElementX/Sources/Other/AccessibilityIdentifiers.swift index e78721e64a..df2c1d07d6 100644 --- a/ElementX/Sources/Other/AccessibilityIdentifiers.swift +++ b/ElementX/Sources/Other/AccessibilityIdentifiers.swift @@ -126,6 +126,9 @@ enum A11yIdentifiers { let timelineItemActionMenu = "room-timeline_item_action_menu" let joinCall = "room-join_call" let scrollToBottom = "room-scroll_to_bottom" + + let messageComposer = "room-message_composer" + let sendButton = "room-send_button" let composerToolbar = ComposerToolbar() diff --git a/ElementX/Sources/Other/ProcessInfo.swift b/ElementX/Sources/Other/Extensions/ProcessInfo.swift similarity index 100% rename from ElementX/Sources/Other/ProcessInfo.swift rename to ElementX/Sources/Other/Extensions/ProcessInfo.swift diff --git a/ElementX/Sources/Other/Logging/RustTracing.swift b/ElementX/Sources/Other/Logging/RustTracing.swift index 243d2d9e81..8e4c0b9b32 100644 --- a/ElementX/Sources/Other/Logging/RustTracing.swift +++ b/ElementX/Sources/Other/Logging/RustTracing.swift @@ -13,7 +13,13 @@ enum RustTracing { /// name and other log management metadata during rotation. static let filePrefix = "console" /// The directory that stores all of the log files. - static var logsDirectory: URL { .appGroupContainerDirectory } + static var logsDirectory: URL { + if ProcessInfo.isRunningIntegrationTests { + "/Users/Shared" + } else { + .appGroupContainerDirectory + } + } private(set) static var currentTracingConfiguration: TracingConfiguration? static func setup(configuration: TracingConfiguration) { @@ -23,7 +29,15 @@ enum RustTracing { // as the app is unlikely to be running continuously. let maxFiles: UInt64 = 24 * 7 - setupTracing(config: .init(filter: configuration.filter, + // Log everything on integration tests to check whether + // the logs contain any sensitive data. See `UserFlowTests.swift` + let filter = if ProcessInfo.isRunningIntegrationTests { + TracingConfiguration(logLevel: .trace, target: nil).filter + } else { + configuration.filter + } + + setupTracing(config: .init(filter: filter, writeToStdoutOrSystem: true, writeToFiles: .init(path: logsDirectory.path(percentEncoded: false), filePrefix: configuration.fileName, diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift index 4f20be1a38..bf61858663 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift @@ -143,6 +143,7 @@ struct ComposerToolbar: View { .disabled(context.viewState.sendButtonDisabled) .animation(.linear(duration: 0.1).disabledDuringTests(), value: context.viewState.sendButtonDisabled) .keyboardShortcut(.return, modifiers: [.command]) + .accessibilityIdentifier(A11yIdentifiers.roomScreen.sendButton) } private var messageComposer: some View { @@ -174,6 +175,7 @@ struct ComposerToolbar: View { .focused($composerFocused) .padding(.leading, context.composerFormattingEnabled ? 7 : 0) .padding(.trailing, context.composerFormattingEnabled ? 4 : 0) + .accessibilityIdentifier(A11yIdentifiers.roomScreen.messageComposer) .onTapGesture { guard !composerFocused else { return } composerFocused = true diff --git a/IntegrationTests/Sources/UserFlowTests.swift b/IntegrationTests/Sources/UserFlowTests.swift index 46e4e0f085..a5f78038d1 100644 --- a/IntegrationTests/Sources/UserFlowTests.swift +++ b/IntegrationTests/Sources/UserFlowTests.swift @@ -8,6 +8,9 @@ import XCTest class UserFlowTests: XCTestCase { + private static let integrationTestsRoomName = "Element X iOS Integration Tests" + private static let integrationTestsMessage = "Go down in flames!" + private var app: XCUIApplication! override func setUp() { @@ -16,15 +19,28 @@ class UserFlowTests: XCTestCase { } func testUserFlow() { + checkRoomFlows() + checkSettings() checkRoomCreation() - // Open the first room in the list. - let firstRoom = app.buttons.matching(NSPredicate(format: "identifier BEGINSWITH %@", A11yIdentifiers.homeScreen.roomNamePrefix)).firstMatch + app.logout() + } + + // Assumes app is on the home screen + private func checkRoomFlows() { + // Search for the special test room + let searchField = app.searchFields.firstMatch + searchField.clearAndTypeText(Self.integrationTestsRoomName) + + // And open it + let firstRoom = app.buttons.matching(NSPredicate(format: "identifier CONTAINS %@", Self.integrationTestsRoomName)).firstMatch XCTAssertTrue(firstRoom.waitForExistence(timeout: 10.0)) firstRoom.tap() + sendMessages() + checkPhotoSharing() checkDocumentSharing() @@ -35,21 +51,58 @@ class UserFlowTests: XCTestCase { checkRoomDetails() - app.logout() + // Go back to the room list + tapOnBackButton("Chats") + + // Cancel initial the room search + let searchCancelButton = app.buttons["Cancel"].firstMatch + XCTAssertTrue(searchCancelButton.waitForExistence(timeout: 10.0)) + searchCancelButton.forceTap() + } + + private func sendMessages() { + var composerTextField = app.textViews[A11yIdentifiers.roomScreen.messageComposer].firstMatch + XCTAssertTrue(composerTextField.waitForExistence(timeout: 10.0)) + composerTextField.clearAndTypeText(Self.integrationTestsMessage) + + var sendButton = app.buttons[A11yIdentifiers.roomScreen.sendButton].firstMatch + XCTAssertTrue(sendButton.waitForExistence(timeout: 10.0)) + sendButton.tap() + + sleep(10) // Wait for the message to be sent + + // Switch to the rich text editor + tapOnMenu(A11yIdentifiers.roomScreen.composerToolbar.openComposeOptions) + tapOnButton(A11yIdentifiers.roomScreen.attachmentPickerTextFormatting) + + composerTextField = app.textViews[A11yIdentifiers.roomScreen.messageComposer].firstMatch + XCTAssertTrue(composerTextField.waitForExistence(timeout: 10.0)) + composerTextField.clearAndTypeText(Self.integrationTestsMessage) + + sendButton = app.buttons[A11yIdentifiers.roomScreen.sendButton].firstMatch + XCTAssertTrue(sendButton.waitForExistence(timeout: 10.0)) + sendButton.tap() + + sleep(5) // Wait for the message to be sent + + // Close the formatting options + app.buttons[A11yIdentifiers.roomScreen.composerToolbar.closeFormattingOptions].tap() } private func checkPhotoSharing() { - // Open attachments picker tapOnMenu(A11yIdentifiers.roomScreen.composerToolbar.openComposeOptions) - - // Open photo library picker tapOnButton(A11yIdentifiers.roomScreen.attachmentPickerPhotoLibrary) + sleep(10) // Wait for the picker to load + // Tap on the second image. First one is always broken on simulators. let secondImage = app.scrollViews.images.element(boundBy: 1) - XCTAssertTrue(secondImage.waitForExistence(timeout: 10.0)) // Photo library takes a bit to load + XCTAssertTrue(secondImage.waitForExistence(timeout: 20.0)) // Photo library takes a bit to load secondImage.tap() + // Wait for the image to be processed and the new screen to appear + sleep(10) + // Cancel the upload flow tapOnButton("Cancel", waitForDisappearance: true) } @@ -58,6 +111,8 @@ class UserFlowTests: XCTestCase { tapOnMenu(A11yIdentifiers.roomScreen.composerToolbar.openComposeOptions) tapOnButton(A11yIdentifiers.roomScreen.attachmentPickerDocuments) + sleep(10) // Wait for the picker to load + tapOnButton("Cancel", waitForDisappearance: true) } @@ -65,6 +120,8 @@ class UserFlowTests: XCTestCase { tapOnMenu(A11yIdentifiers.roomScreen.composerToolbar.openComposeOptions) tapOnButton(A11yIdentifiers.roomScreen.attachmentPickerLocation) + sleep(10) // Wait for the picker to load + // The order of the alerts is a bit of a mistery so try twice allowLocationPermissionOnce() @@ -139,9 +196,6 @@ class UserFlowTests: XCTestCase { // Go back to the room tapOnBackButton("Chat") - - // Go back to the room list - tapOnBackButton("Chats") } private func checkSettings() { diff --git a/NSE/SupportingFiles/target.yml b/NSE/SupportingFiles/target.yml index fae28e976b..417469aa13 100644 --- a/NSE/SupportingFiles/target.yml +++ b/NSE/SupportingFiles/target.yml @@ -87,6 +87,7 @@ targets: - path: ../../ElementX/Sources/Other/Extensions/ImageCache.swift - path: ../../ElementX/Sources/Other/Extensions/LayoutDirection.swift - path: ../../ElementX/Sources/Other/Extensions/NSRegularExpresion.swift + - path: ../../ElementX/Sources/Other/Extensions/ProcessInfo.swift - path: ../../ElementX/Sources/Other/Extensions/String.swift - path: ../../ElementX/Sources/Other/Extensions/Task.swift - path: ../../ElementX/Sources/Other/Extensions/UNNotificationContent.swift @@ -112,3 +113,4 @@ targets: - path: ../../ElementX/Sources/Services/Room/RoomSummary/RoomMessageEventStringBuilder.swift - path: ../../ElementX/Sources/Services/UserSession/RestorationToken.swift - path: ../../ElementX/Sources/Services/UserSession/SessionDirectories.swift + - path: ../../ElementX/Sources/UITests/UITestsScreenIdentifier.swift From 23ab453a1e30e3a77bf350c2f9c6ca5e2e39c2c7 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 26 Sep 2024 13:24:46 +0200 Subject: [PATCH 007/114] crypto: rename invisible crypto flag to deviceIsolationMode (#3331) --- ElementX/Sources/Application/AppSettings.swift | 10 +++++----- ElementX/Sources/Other/Extensions/ClientBuilder.swift | 4 ++-- .../DeveloperOptionsScreenModels.swift | 2 +- .../View/DeveloperOptionsScreen.swift | 6 +++--- .../Authentication/AuthenticationClientBuilder.swift | 2 +- .../Services/UserSession/UserSessionStore.swift | 2 +- NSE/Sources/Other/NSEUserSession.swift | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index 44a07da5f3..d2985c8c75 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -11,7 +11,7 @@ import SwiftUI // Common settings between app and NSE protocol CommonSettingsProtocol { var logLevel: TracingConfiguration.LogLevel { get } - var invisibleCryptoEnabled: Bool { get } + var enableOnlySignedDeviceIsolationMode: Bool { get } } /// Store Element specific app settings. @@ -43,7 +43,7 @@ final class AppSettings { case publicSearchEnabled case fuzzyRoomListSearchEnabled case pinningEnabled - case invisibleCryptoEnabled + case enableOnlySignedDeviceIsolationMode } private static var suiteName: String = InfoPlistReader.main.appGroupIdentifier @@ -285,9 +285,9 @@ final class AppSettings { @UserPreference(key: UserDefaultsKeys.logLevel, defaultValue: TracingConfiguration.LogLevel.info, storageType: .userDefaults(store)) var logLevel - /// Configuration to enable invisible crypto. In this mode only devices signed by their owner will be considered in e2ee rooms. - @UserPreference(key: UserDefaultsKeys.invisibleCryptoEnabled, defaultValue: false, storageType: .userDefaults(store)) - var invisibleCryptoEnabled + /// Configuration to enable only signed device isolation mode for crypto. In this mode only devices signed by their owner will be considered in e2ee rooms. + @UserPreference(key: UserDefaultsKeys.enableOnlySignedDeviceIsolationMode, defaultValue: false, storageType: .userDefaults(store)) + var enableOnlySignedDeviceIsolationMode } extension AppSettings: CommonSettingsProtocol { } diff --git a/ElementX/Sources/Other/Extensions/ClientBuilder.swift b/ElementX/Sources/Other/Extensions/ClientBuilder.swift index 586fa4c201..957eb852d7 100644 --- a/ElementX/Sources/Other/Extensions/ClientBuilder.swift +++ b/ElementX/Sources/Other/Extensions/ClientBuilder.swift @@ -15,7 +15,7 @@ extension ClientBuilder { slidingSync: ClientBuilderSlidingSync, sessionDelegate: ClientSessionDelegate, appHooks: AppHooks, - invisibleCryptoEnabled: Bool) -> ClientBuilder { + enableOnlySignedDeviceIsolationMode: Bool) -> ClientBuilder { var builder = ClientBuilder() .enableCrossProcessRefreshLock(processId: InfoPlistReader.main.bundleIdentifier, sessionDelegate: sessionDelegate) .userAgent(userAgent: UserAgentBuilder.makeASCIIUserAgent()) @@ -34,7 +34,7 @@ extension ClientBuilder { .backupDownloadStrategy(backupDownloadStrategy: .afterDecryptionFailure) .autoEnableBackups(autoEnableBackups: true) - if invisibleCryptoEnabled { + if enableOnlySignedDeviceIsolationMode { builder = builder.roomKeyRecipientStrategy(strategy: CollectStrategy.identityBasedStrategy) } else { builder = builder.roomKeyRecipientStrategy(strategy: .deviceBasedStrategy(onlyAllowTrustedDevices: false, errorOnVerifiedUserProblem: true)) diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift index 8ad26e512f..8a3d11809a 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift @@ -47,7 +47,7 @@ protocol DeveloperOptionsProtocol: AnyObject { var elementCallBaseURLOverride: URL? { get set } var fuzzyRoomListSearchEnabled: Bool { get set } var pinningEnabled: Bool { get set } - var invisibleCryptoEnabled: Bool { get set } + var enableOnlySignedDeviceIsolationMode: Bool { get set } } extension AppSettings: DeveloperOptionsProtocol { } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift index 7f9217f665..24152f5beb 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -53,14 +53,14 @@ struct DeveloperOptionsScreen: View { } Section { - Toggle(isOn: $context.invisibleCryptoEnabled) { - Text("Enabled Invisible Crypto") + Toggle(isOn: $context.enableOnlySignedDeviceIsolationMode) { + Text("Exclude not secure devices when sending/receiving messages") Text("Requires app reboot") } } header: { Text("Trust and Decoration") } footer: { - Text("This setting controls how end-to-end encryption (E2EE) keys are shared. Enabling it will prevent the inclusion of devices that have not been explicitly verified by their owners.") + Text("This setting controls how end-to-end encryption (E2EE) keys are exchanged. Enabling it will prevent the inclusion of devices that have not been explicitly verified by their owners.") } Section { diff --git a/ElementX/Sources/Services/Authentication/AuthenticationClientBuilder.swift b/ElementX/Sources/Services/Authentication/AuthenticationClientBuilder.swift index dee843b5f5..1c448eae92 100644 --- a/ElementX/Sources/Services/Authentication/AuthenticationClientBuilder.swift +++ b/ElementX/Sources/Services/Authentication/AuthenticationClientBuilder.swift @@ -78,7 +78,7 @@ struct AuthenticationClientBuilder: AuthenticationClientBuilderProtocol { slidingSync: slidingSync, sessionDelegate: clientSessionDelegate, appHooks: appHooks, - invisibleCryptoEnabled: appSettings.invisibleCryptoEnabled) + enableOnlySignedDeviceIsolationMode: appSettings.enableOnlySignedDeviceIsolationMode) .sessionPaths(dataPath: sessionDirectories.dataPath, cachePath: sessionDirectories.cachePath) .passphrase(passphrase: passphrase) diff --git a/ElementX/Sources/Services/UserSession/UserSessionStore.swift b/ElementX/Sources/Services/UserSession/UserSessionStore.swift index ab38d90a9c..310475032a 100644 --- a/ElementX/Sources/Services/UserSession/UserSessionStore.swift +++ b/ElementX/Sources/Services/UserSession/UserSessionStore.swift @@ -125,7 +125,7 @@ class UserSessionStore: UserSessionStoreProtocol { slidingSync: .restored, sessionDelegate: keychainController, appHooks: appHooks, - invisibleCryptoEnabled: appSettings.invisibleCryptoEnabled) + enableOnlySignedDeviceIsolationMode: appSettings.enableOnlySignedDeviceIsolationMode) .sessionPaths(dataPath: credentials.restorationToken.sessionDirectories.dataPath, cachePath: credentials.restorationToken.sessionDirectories.cachePath) .username(username: credentials.userID) diff --git a/NSE/Sources/Other/NSEUserSession.swift b/NSE/Sources/Other/NSEUserSession.swift index 54e915f193..6a2d99dbc2 100644 --- a/NSE/Sources/Other/NSEUserSession.swift +++ b/NSE/Sources/Other/NSEUserSession.swift @@ -34,7 +34,7 @@ final class NSEUserSession { slidingSync: .restored, sessionDelegate: clientSessionDelegate, appHooks: appHooks, - invisibleCryptoEnabled: appSettings.invisibleCryptoEnabled) + enableOnlySignedDeviceIsolationMode: appSettings.enableOnlySignedDeviceIsolationMode) .sessionPaths(dataPath: credentials.restorationToken.sessionDirectories.dataPath, cachePath: credentials.restorationToken.sessionDirectories.cachePath) .username(username: credentials.userID) From 4d7d687954ea6e3905d86610b5f228e6a7ea8af8 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:09:01 +0100 Subject: [PATCH 008/114] =?UTF-8?q?Start=20fixing=20flakey=20tests=20?= =?UTF-8?q?=E2=9D=84=EF=B8=8F=20(#3329)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Wait longer on authentication flow tests. * Move default perceptualPrecision value into the snapshot preferences. * Delay snapshots *before* setting up the test. * Reset the simulators on GitHub before running? * Remove a test that is now handled by Rust. * Fix a test that was yielding. --- .github/workflows/integration-tests.yml | 5 +- .github/workflows/unit_tests.yml | 3 + .github/workflows/unit_tests_enterprise.yml | 5 +- .../Other/Extensions/Snapshotting.swift | 4 +- PreviewTests/Sources/PreviewTests.swift | 89 ++++++++++--------- ...ojiPickerScreen-iPhone-16-en-GB.Screen.png | 4 +- ...jiPickerScreen-iPhone-16-pseudo.Screen.png | 4 +- ...t_globalSearchScreen-iPhone-16-en-GB.1.png | 4 +- ..._globalSearchScreen-iPhone-16-pseudo.1.png | 4 +- .../test_homeScreen-iPhone-16-en-GB.Empty.png | 4 +- ...test_homeScreen-iPhone-16-en-GB.Loaded.png | 4 +- ...test_homeScreen-iPhone-16-pseudo.Empty.png | 4 +- ...est_homeScreen-iPhone-16-pseudo.Loaded.png | 4 +- ...olderScreen-iPhone-16-en-GB.Split-View.png | 4 +- ...lderScreen-iPhone-16-pseudo.Split-View.png | 4 +- ...ailsScreen-iPhone-16-en-GB.Simple-Room.png | 4 +- ...ilsScreen-iPhone-16-pseudo.Simple-Room.png | 4 +- ...roomDirectorySearchScreen-iPad-en-GB.1.png | 4 +- ...oomDirectorySearchScreen-iPad-pseudo.1.png | 4 +- ...irectorySearchScreen-iPhone-16-en-GB.1.png | 4 +- ...rectorySearchScreen-iPhone-16-pseudo.1.png | 4 +- ...istScreen-iPhone-16-en-GB.Admin-Banned.png | 4 +- ...een-iPhone-16-en-GB.Admin-Empty-Banned.png | 4 +- ...stScreen-iPhone-16-en-GB.Admin-Members.png | 4 +- ...bersListScreen-iPhone-16-en-GB.Invites.png | 4 +- ...mbersListScreen-iPhone-16-en-GB.Member.png | 4 +- ...stScreen-iPhone-16-pseudo.Admin-Banned.png | 4 +- ...en-iPhone-16-pseudo.Admin-Empty-Banned.png | 4 +- ...tScreen-iPhone-16-pseudo.Admin-Members.png | 4 +- ...ersListScreen-iPhone-16-pseudo.Invites.png | 4 +- ...bersListScreen-iPhone-16-pseudo.Member.png | 4 +- .../test_settingsScreen-iPhone-16-en-GB.1.png | 4 +- ...test_settingsScreen-iPhone-16-pseudo.1.png | 4 +- ...erView-iPhone-16-en-GB.Pinned-messages.png | 4 +- ...rView-iPhone-16-pseudo.Pinned-messages.png | 4 +- ...imelineItemDebugView-iPhone-16-en-GB.1.png | 4 +- ...melineItemDebugView-iPhone-16-pseudo.1.png | 4 +- ...iceMessagePreviewComposer-iPad-en-GB.1.png | 4 +- ...ceMessagePreviewComposer-iPad-pseudo.1.png | 4 +- ...ssagePreviewComposer-iPhone-16-en-GB.1.png | 4 +- ...sagePreviewComposer-iPhone-16-pseudo.1.png | 4 +- .../ComposerToolbarViewModelTests.swift | 6 +- .../Sources/TimelineViewModelTests.swift | 33 ------- .../UserSessionFlowCoordinatorTests.swift | 2 +- 44 files changed, 138 insertions(+), 153 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 4b0661b05a..e446a5da9a 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -29,7 +29,10 @@ jobs: - name: Setup environment run: source ci_scripts/ci_common.sh && setup_github_actions_environment - + + - name: Reset simulators + run: SNAPSHOT_FORCE_DELETE=1 bundle exec fastlane snapshot reset_simulators + - name: Run tests run: bundle exec fastlane integration_tests env: diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 63fa5e54ed..1d6206923e 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -35,6 +35,9 @@ jobs: - name: SwiftFormat run: swiftformat --lint . + + - name: Reset simulators + run: SNAPSHOT_FORCE_DELETE=1 bundle exec fastlane snapshot reset_simulators - name: Run tests run: bundle exec fastlane unit_tests diff --git a/.github/workflows/unit_tests_enterprise.yml b/.github/workflows/unit_tests_enterprise.yml index 8c195bf915..8df5b704c7 100644 --- a/.github/workflows/unit_tests_enterprise.yml +++ b/.github/workflows/unit_tests_enterprise.yml @@ -44,7 +44,10 @@ jobs: - name: SwiftFormat run: swiftformat --lint . - + + - name: Reset simulators + run: SNAPSHOT_FORCE_DELETE=1 bundle exec fastlane snapshot reset_simulators + - name: Run tests run: bundle exec fastlane unit_tests skip_previews:true diff --git a/ElementX/Sources/Other/Extensions/Snapshotting.swift b/ElementX/Sources/Other/Extensions/Snapshotting.swift index 77a968a15f..7de5ca95bb 100644 --- a/ElementX/Sources/Other/Extensions/Snapshotting.swift +++ b/ElementX/Sources/Other/Extensions/Snapshotting.swift @@ -33,7 +33,7 @@ public struct SnapshotPrecisionPreferenceKey: PreferenceKey { } public struct SnapshotPerceptualPrecisionPreferenceKey: PreferenceKey { - public static var defaultValue: Float = 1.0 + public static var defaultValue: Float = 0.98 public static func reduce(value: inout Float, nextValue: () -> Float) { value = nextValue() @@ -50,7 +50,7 @@ public extension SwiftUI.View { /// - precision: The percentage of pixels that must match. /// - perceptualPrecision: The percentage a pixel must match the source pixel to be considered a match. 98-99% mimics the precision of the human eye. @inlinable - func snapshotPreferences(delay: TimeInterval = .zero, precision: Float = 1.0, perceptualPrecision: Float = 1.0) -> some SwiftUI.View { + func snapshotPreferences(delay: TimeInterval = .zero, precision: Float = 1.0, perceptualPrecision: Float = 0.98) -> some SwiftUI.View { preference(key: SnapshotDelayPreferenceKey.self, value: delay) .preference(key: SnapshotPrecisionPreferenceKey.self, value: precision) .preference(key: SnapshotPerceptualPrecisionPreferenceKey.self, value: perceptualPrecision) diff --git a/PreviewTests/Sources/PreviewTests.swift b/PreviewTests/Sources/PreviewTests.swift index 4d7148508d..5fd769481a 100644 --- a/PreviewTests/Sources/PreviewTests.swift +++ b/PreviewTests/Sources/PreviewTests.swift @@ -11,6 +11,7 @@ import XCTest @testable import ElementX @testable import SnapshotTesting +@MainActor class PreviewTests: XCTestCase { private let deviceConfig: ViewImageConfig = .iPhoneX private let simulatorDevice: String? = "iPhone14,6" // iPhone SE 3rd Generation @@ -50,6 +51,22 @@ class PreviewTests: XCTestCase { // MARK: - Snapshots func assertSnapshots(matching preview: _Preview, testName: String = #function) { + let preferences = SnapshotPreferences() + + let preferenceReadingView = preview.content + .onPreferenceChange(SnapshotDelayPreferenceKey.self) { preferences.delay = $0 } + .onPreferenceChange(SnapshotPrecisionPreferenceKey.self) { preferences.precision = $0 } + .onPreferenceChange(SnapshotPerceptualPrecisionPreferenceKey.self) { preferences.perceptualPrecision = $0 } + + // Render an image of the view in order to trigger the preference updates to occur. + let imageRenderer = ImageRenderer(content: preferenceReadingView) + _ = imageRenderer.uiImage + + // Delay the test now - a delay after creating the `snapshotView` results in the underlying view not getting updated for snapshotting. + if preferences.delay != .zero { + wait(for: preferences.delay) + } + for deviceName in snapshotDevices { guard var device = PreviewDevice(rawValue: deviceName).snapshotDevice() else { fatalError("Unknown device name: \(deviceName)") @@ -58,12 +75,13 @@ class PreviewTests: XCTestCase { device.safeArea = .one // Ignore specific device display scale let traits = UITraitCollection(displayScale: 2.0) - if let failure = assertSnapshots(matching: AnyView(preview.content), + if let failure = assertSnapshots(matching: preview.content, name: preview.displayName, isScreen: preview.layout == .device, device: device, testName: testName + deviceName + "-" + localeCode, - traits: traits) { + traits: traits, + preferences: preferences) { XCTFail(failure) } } @@ -85,19 +103,12 @@ class PreviewTests: XCTestCase { } private func assertSnapshots(matching view: AnyView, - name: String?, isScreen: Bool, + name: String?, + isScreen: Bool, device: ViewImageConfig, testName: String = #function, - traits: UITraitCollection = .init()) -> String? { - var delay: TimeInterval = 0 - var precision: Float = 1 - var perceptualPrecision: Float = 1 - - let view = view - .onPreferenceChange(SnapshotDelayPreferenceKey.self) { delay = $0 } - .onPreferenceChange(SnapshotPrecisionPreferenceKey.self) { precision = $0 } - .onPreferenceChange(SnapshotPerceptualPrecisionPreferenceKey.self) { perceptualPrecision = $0 } - + traits: UITraitCollection = .init(), + preferences: SnapshotPreferences) -> String? { let matchingView = isScreen ? AnyView(view) : AnyView(view .frame(width: device.size?.width) .fixedSize(horizontal: false, vertical: true) @@ -105,15 +116,27 @@ class PreviewTests: XCTestCase { return withSnapshotTesting(record: recordMode) { verifySnapshot(of: matchingView, - as: .prefireImage(precision: { precision }, - perceptualPrecision: { perceptualPrecision }, - duration: { delay }, + as: .prefireImage(preferences: preferences, layout: isScreen ? .device(config: device) : .sizeThatFits, traits: traits), named: name, testName: testName) } } + + private func wait(for duration: TimeInterval) { + let expectation = XCTestExpectation(description: "Wait") + DispatchQueue.main.asyncAfter(deadline: .now() + duration) { + expectation.fulfill() + } + _ = XCTWaiter.wait(for: [expectation], timeout: duration + 1) + } +} + +private class SnapshotPreferences { + var delay: TimeInterval = 0 + var precision: Float = 1 + var perceptualPrecision: Float = 1 } // MARK: - SnapshotTesting + Extensions @@ -144,9 +167,7 @@ private extension PreviewDevice { private extension Snapshotting where Value: SwiftUI.View, Format == UIImage { static func prefireImage(drawHierarchyInKeyWindow: Bool = false, - precision: @escaping () -> Float, - perceptualPrecision: @escaping () -> Float, - duration: @escaping () -> TimeInterval, + preferences: SnapshotPreferences, layout: SwiftUISnapshotLayout = .sizeThatFits, traits: UITraitCollection = .init()) -> Snapshotting { let config: ViewImageConfig @@ -165,7 +186,7 @@ private extension Snapshotting where Value: SwiftUI.View, Format == UIImage { config = .init(safeArea: .one, size: size, traits: traits) } - return SimplySnapshotting(pathExtension: "png", diffing: .prefireImage(precision: precision, perceptualPrecision: perceptualPrecision, scale: traits.displayScale)) + return SimplySnapshotting(pathExtension: "png", diffing: .prefireImage(preferences: preferences, scale: traits.displayScale)) .asyncPullback { view in var config = config @@ -181,31 +202,19 @@ private extension Snapshotting where Value: SwiftUI.View, Format == UIImage { controller = hostingController } - - return Async { callback in - let strategy = snapshotView(config: config, - drawHierarchyInKeyWindow: drawHierarchyInKeyWindow, - traits: traits, - view: controller.view, - viewController: controller) - - let duration = duration() - if duration != .zero { - let expectation = XCTestExpectation(description: "Wait") - DispatchQueue.main.asyncAfter(deadline: .now() + duration) { - expectation.fulfill() - } - _ = XCTWaiter.wait(for: [expectation], timeout: duration + 1) - } - strategy.run(callback) - } + + return snapshotView(config: config, + drawHierarchyInKeyWindow: drawHierarchyInKeyWindow, + traits: traits, + view: controller.view, + viewController: controller) } } } private extension Diffing where Value == UIImage { - static func prefireImage(precision: @escaping () -> Float, perceptualPrecision: @escaping () -> Float, scale: CGFloat?) -> Diffing { - lazy var originalDiffing = Diffing.image(precision: precision(), perceptualPrecision: 0.98, scale: scale) + static func prefireImage(preferences: SnapshotPreferences, scale: CGFloat?) -> Diffing { + lazy var originalDiffing = Diffing.image(precision: preferences.precision, perceptualPrecision: preferences.perceptualPrecision, scale: scale) return Diffing(toData: { originalDiffing.toData($0) }, fromData: { originalDiffing.fromData($0) }, diff: { originalDiffing.diff($0, $1) }) diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_emojiPickerScreen-iPhone-16-en-GB.Screen.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_emojiPickerScreen-iPhone-16-en-GB.Screen.png index 670299a350..afcfe3ebff 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_emojiPickerScreen-iPhone-16-en-GB.Screen.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_emojiPickerScreen-iPhone-16-en-GB.Screen.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3181bdcf7d91c89da8af03b325c9ca5880c3e0a97c67bfb74da7c0c22048b19 -size 36664 +oid sha256:719a9f2d4a69ff30f6eedf146bb6cef2e79e4a6dabcdcc10a8429435531eb75d +size 362302 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_emojiPickerScreen-iPhone-16-pseudo.Screen.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_emojiPickerScreen-iPhone-16-pseudo.Screen.png index 35e93f0c2b..680877471f 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_emojiPickerScreen-iPhone-16-pseudo.Screen.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_emojiPickerScreen-iPhone-16-pseudo.Screen.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0320762f030bc3ceddd164fb0fff5ad4743bbf2667039921a15a59bb2366b0a5 -size 38589 +oid sha256:7bd897cd6531b60437deda1a94f0a87c21e5ea33fdaf483eb699e99a4fd44908 +size 365397 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_globalSearchScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_globalSearchScreen-iPhone-16-en-GB.1.png index d2e5314392..f80889fefc 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_globalSearchScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_globalSearchScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13e909870235a90145b7fc49967383e92a0bf45917f020cc793dcbde87fe2bfb -size 91414 +oid sha256:656ab8534b3b28a11a09b7ee3bd735de4f9173687006d8346093ec8fa70caf54 +size 88511 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_globalSearchScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_globalSearchScreen-iPhone-16-pseudo.1.png index 7dab18b499..84e27fc201 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_globalSearchScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_globalSearchScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a80a970d60708545d81c9c35be059484be1a6f686a53b3eafbdb831f5ef6729d -size 89319 +oid sha256:6dece45267d9514a73241f045de87eb76d7d87ccc236aed3d114ccfbe9507d74 +size 88837 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreen-iPhone-16-en-GB.Empty.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreen-iPhone-16-en-GB.Empty.png index 3fe0b247b1..6626ad8098 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreen-iPhone-16-en-GB.Empty.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreen-iPhone-16-en-GB.Empty.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6136573195a4b3389a9d06966e5f0e9a400d6ea247b2ff5210dcb0e139a957b5 -size 51143 +oid sha256:d96438c624f5b1bcd690c82bacb133e08a830b0ee1a07f888fe845a60bfa1e6a +size 50998 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreen-iPhone-16-en-GB.Loaded.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreen-iPhone-16-en-GB.Loaded.png index 8fb9ed0f80..45e17c03c9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreen-iPhone-16-en-GB.Loaded.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreen-iPhone-16-en-GB.Loaded.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fca553c7fbb54deea774d261146a597ea426e00529a7a7cd7d64efabd8b7a1e6 -size 178424 +oid sha256:5090cca7e3b49782ad27006c8c6980f264f4e493fd8fa2b35c0a1ee6164b0b3a +size 178329 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreen-iPhone-16-pseudo.Empty.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreen-iPhone-16-pseudo.Empty.png index 7a3b62ec10..4f14f5bfea 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreen-iPhone-16-pseudo.Empty.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreen-iPhone-16-pseudo.Empty.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42b66da0630c9f320e7e1dc1b19261c647c5758e21248f61df5f134fc41e6201 -size 51984 +oid sha256:4c06f6941486a88c19d4b67d7b0afa91678649a535a30b696891fbb0815ac76b +size 61299 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreen-iPhone-16-pseudo.Loaded.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreen-iPhone-16-pseudo.Loaded.png index d70680ecdb..4499b658e6 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreen-iPhone-16-pseudo.Loaded.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreen-iPhone-16-pseudo.Loaded.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c595691dab7881f5dfc46a5051d23fdcf7fb9684c7f1fbe445829fff02b9bbdd -size 181004 +oid sha256:806e066b3632f2a73012c2bea9993f2f5026304235fd5ea072321a5f3db7e4f3 +size 180923 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_placeholderScreen-iPhone-16-en-GB.Split-View.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_placeholderScreen-iPhone-16-en-GB.Split-View.png index 3de9e4f423..c6c657b573 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_placeholderScreen-iPhone-16-en-GB.Split-View.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_placeholderScreen-iPhone-16-en-GB.Split-View.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:305b1b6a9e5816174b64fd2819b79a7489405b0f7eef86d47b4471f6a117ba99 -size 39207 +oid sha256:0d7e551b2dfbec476b1164b49c8b800ab88f2fb3bf0e204f7fae38323ede91b4 +size 17360 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_placeholderScreen-iPhone-16-pseudo.Split-View.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_placeholderScreen-iPhone-16-pseudo.Split-View.png index 3de9e4f423..c6c657b573 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_placeholderScreen-iPhone-16-pseudo.Split-View.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_placeholderScreen-iPhone-16-pseudo.Split-View.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:305b1b6a9e5816174b64fd2819b79a7489405b0f7eef86d47b4471f6a117ba99 -size 39207 +oid sha256:0d7e551b2dfbec476b1164b49c8b800ab88f2fb3bf0e204f7fae38323ede91b4 +size 17360 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.Simple-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.Simple-Room.png index 0f13aae409..abff795aad 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.Simple-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.Simple-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4a39dd36ae824a1de6f05a815e3456f587c29f9b38e8241687d39af5de34aef -size 84853 +oid sha256:1ff2b602ea177f6324527e5531720e718c36e922b7606c0bc67f8c5f8b6a9d23 +size 93986 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.Simple-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.Simple-Room.png index 01de4402c8..6fa8cf6278 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.Simple-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.Simple-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c9805e5c3f1fc3cf5182ba6dbe812bf85835c62b1caedeb2b8b9b96773e8c21 -size 106502 +oid sha256:8e69bbc84760736834b39a253232206e0f98942a9de5bb909b36ea80ffdee4d7 +size 110395 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDirectorySearchScreen-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDirectorySearchScreen-iPad-en-GB.1.png index 68337fae4a..b4b93ae2ed 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDirectorySearchScreen-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDirectorySearchScreen-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:222b8e57adaf6d234c2e6ac026cba0380131c7085d6a4d0e15e6fb349dbea1d8 -size 93186 +oid sha256:39c099e692cac9570866390ae3fa3e402cdd14f19de1df4ee63b206e84ac7e40 +size 81301 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDirectorySearchScreen-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDirectorySearchScreen-iPad-pseudo.1.png index 9fe58baf32..3a984cb4ca 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDirectorySearchScreen-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDirectorySearchScreen-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1040add34f78fa95ae396d7ce95df29a7f03c73ade149a5d0efa9418335531a6 -size 95828 +oid sha256:35bd1fba8ab30bb7d08080fe06cfadc8d0838a484bb9096c8cefd85b6af70543 +size 85637 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDirectorySearchScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDirectorySearchScreen-iPhone-16-en-GB.1.png index d0e1cc4330..e85bd9e19c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDirectorySearchScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDirectorySearchScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fdaf986c1521ecb43b117f3197053f589e66e3e496a51d824629d8253653a713 -size 49876 +oid sha256:9658986db1f622979c311716b815190aa409fd70cd71d52ae9cedc5a2a2be60d +size 39632 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDirectorySearchScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDirectorySearchScreen-iPhone-16-pseudo.1.png index bd143b2b96..50d3464c38 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDirectorySearchScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDirectorySearchScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4840a11a65bc0a611c5cb2b93883cf707f1cbd686359909704398ed87eced86d -size 52399 +oid sha256:73d404858351ed5066e5a5b9c78067c149cbf4f5b835d143172eb047e7bc7299 +size 43558 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Admin-Banned.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Admin-Banned.png index 6c320b9828..4ca9247c9d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Admin-Banned.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Admin-Banned.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f9871ae3306a42d01602c7a8cc2153131c1bd9c91f13129c3bdf0d8ece816c6 -size 68400 +oid sha256:9716ab5589ae525ee33f7c8d56c8de6d75eadbd58de55be381d7b6d1d2fa72d2 +size 68373 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Admin-Empty-Banned.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Admin-Empty-Banned.png index fb947d7f96..5db0a03b85 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Admin-Empty-Banned.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Admin-Empty-Banned.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a96b9608b3fbdef660995e45324eb1c5b5cbdf18cae679f17cded799aa9d1c9 -size 48922 +oid sha256:cc8c8a45449ae6a45e03cc55abbb7daacc73437723386278049702d075b2dfec +size 48907 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Admin-Members.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Admin-Members.png index 352123acf0..42a29274d7 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Admin-Members.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Admin-Members.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58820cced75f459149aa85f2b32ddc9f6311fcb0795c5e8801b45c228751d698 -size 83703 +oid sha256:0cb4f935ad16ab0e44d65f0f3865865ea493be1c882ac6c9dfa0b9ef1ce2b7de +size 83685 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Invites.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Invites.png index 0d7dfda678..ff9a367829 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Invites.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Invites.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b022726222227a8f90998e54d58bcd7ab666089d1d7dff2724f21b11daf2b554 -size 86214 +oid sha256:8b7a2e7b7d6f76a6e2a99608791995c03b938c3157386ccb874e0351989be485 +size 86192 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Member.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Member.png index 9c92bf724b..cea8e3bf1f 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Member.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-en-GB.Member.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7925fd0beeac60deea2db678b17265aedee906252d97d0214f641c2a21bf5fb -size 37037 +oid sha256:5b50eec37266cca3aaf324a6b477aba2c0e10ac37540ace2f84d9cfcc9b914c4 +size 77856 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Admin-Banned.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Admin-Banned.png index a44ac201be..e3e36b9ee2 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Admin-Banned.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Admin-Banned.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de46b50e23ecb747e9d60c9448b60a977571af8e74ce4f5ca20e440189ce36f7 -size 73574 +oid sha256:9d2c1fd3265cdb97ffe95cf0fd795bc0751c86de6d1e6f5228000d8daddbffb3 +size 73543 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Admin-Empty-Banned.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Admin-Empty-Banned.png index 4bacc94c2d..5804ddf3ad 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Admin-Empty-Banned.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Admin-Empty-Banned.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d8830e48e24781b958a21e9ce56ac7f12583cf237fc4a40cff57a4a8fb4e5e2 -size 59454 +oid sha256:4f81f36ffc30df200c36a8bb8c47e47564951262079850d3f3baffdac96a61a2 +size 59461 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Admin-Members.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Admin-Members.png index e76a1aec2e..97419a8ff9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Admin-Members.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Admin-Members.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:54a2cde62d75459851c485b761aa5161cbb4176560eeaab69fcfa520e362bb11 -size 93420 +oid sha256:65d99cd60b4113ae8d91b8602b39800ea2643160952f1c5d59f9dd28628ea9b2 +size 93372 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Invites.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Invites.png index 6de78ba4b3..b5753e4d3f 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Invites.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Invites.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f4853074c004e451d4d88453b977064c25b59a29803acb1ea5b38882c7860ad3 -size 96319 +oid sha256:e1487324353bcdd77881ba9ca264a05d7a932bb857d4f61accba7c28ea65d871 +size 96301 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Member.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Member.png index 2b199a18e0..c7d567092b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Member.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMembersListScreen-iPhone-16-pseudo.Member.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4758385db96e9d880f7ff670f1b312964b27910ba1c55ecafa9a451c964ab474 -size 40619 +oid sha256:027180a5377922209a9b58f36c2ab352ae5f58dad3fa653982276ce07d155d38 +size 86305 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPhone-16-en-GB.1.png index 8d385346ec..44b007a17c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce5e1bd5e6f7325fdea0332cce899a8f0c1439d971d159324d48f89ed3a58ac6 -size 106225 +oid sha256:1062bd3adf6d606bb0d1f8c693bff1e66ae6110e0bf5a6f65037f2b980e6e39e +size 111198 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPhone-16-pseudo.1.png index 8f1bfb9567..8703632b7e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3755ae2d427b03ba0f591ac1358c8e74c14cb378a20a53560e342b6077562837 -size 123000 +oid sha256:1a3e452e09cb3ab4908ad93c1802ceae66dc60334a2494fce8c34ea58bc2ab7e +size 127950 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Pinned-messages.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Pinned-messages.png index b97c73e538..3ccfc81eac 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Pinned-messages.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Pinned-messages.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ba473ce453cdd3f505411fb9cfcafb8409d073d0c737ce3716f2c251acb0bf0 -size 494165 +oid sha256:03de5c32a0b0e47ea2b2d54214048fa1f9dadd7a7bed970af935d1c1ca0cf7ce +size 445294 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Pinned-messages.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Pinned-messages.png index b97c73e538..3ccfc81eac 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Pinned-messages.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Pinned-messages.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ba473ce453cdd3f505411fb9cfcafb8409d073d0c737ce3716f2c251acb0bf0 -size 494165 +oid sha256:03de5c32a0b0e47ea2b2d54214048fa1f9dadd7a7bed970af935d1c1ca0cf7ce +size 445294 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemDebugView-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemDebugView-iPhone-16-en-GB.1.png index bed3e4ee1e..30b892a7ad 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemDebugView-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemDebugView-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8a1c81979803d04546d37f153017199c560cffe91f12d132449784849f00903 -size 69227 +oid sha256:f8122c559a90fd4425a7117960fad021482fa2c93585a72f6f2814d619d7bf2d +size 69384 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemDebugView-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemDebugView-iPhone-16-pseudo.1.png index 316be7a3bf..7211e600df 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemDebugView-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemDebugView-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69d3cf0672978bc46113354cb6b7ead0b815f1db78a51d1e355a2d7c06fcc7cc -size 70107 +oid sha256:f0f1970da3feb417beeb4a007406510a33a8d204fece27109d883b1b1bb76ea7 +size 70243 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_voiceMessagePreviewComposer-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_voiceMessagePreviewComposer-iPad-en-GB.1.png index dee8424e61..6c40bc49dc 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_voiceMessagePreviewComposer-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_voiceMessagePreviewComposer-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a319198ac7b9f91e4ed6bd9411a73fd9b7cabf80483507a4cf975426305379b2 -size 71910 +oid sha256:641bf4dd2d18f7ff9f72d9aadf574fc276deda99525cd6c933506043c60a6856 +size 71904 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_voiceMessagePreviewComposer-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_voiceMessagePreviewComposer-iPad-pseudo.1.png index dee8424e61..6c40bc49dc 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_voiceMessagePreviewComposer-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_voiceMessagePreviewComposer-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a319198ac7b9f91e4ed6bd9411a73fd9b7cabf80483507a4cf975426305379b2 -size 71910 +oid sha256:641bf4dd2d18f7ff9f72d9aadf574fc276deda99525cd6c933506043c60a6856 +size 71904 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_voiceMessagePreviewComposer-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_voiceMessagePreviewComposer-iPhone-16-en-GB.1.png index 0fce89be22..7de4341245 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_voiceMessagePreviewComposer-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_voiceMessagePreviewComposer-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fe33b9011954c24dfe13514927cedd7b076f43690f163a13b537a80f998fc8c -size 31324 +oid sha256:31b749bf22b17db53d6a0128b366a73238ee89e7593c71c445e911608835a588 +size 31069 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_voiceMessagePreviewComposer-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_voiceMessagePreviewComposer-iPhone-16-pseudo.1.png index 0fce89be22..7de4341245 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_voiceMessagePreviewComposer-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_voiceMessagePreviewComposer-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fe33b9011954c24dfe13514927cedd7b076f43690f163a13b537a80f998fc8c -size 31324 +oid sha256:31b749bf22b17db53d6a0128b366a73238ee89e7593c71c445e911608835a588 +size 31069 diff --git a/UnitTests/Sources/ComposerToolbarViewModelTests.swift b/UnitTests/Sources/ComposerToolbarViewModelTests.swift index 162ac0d97f..d788534884 100644 --- a/UnitTests/Sources/ComposerToolbarViewModelTests.swift +++ b/UnitTests/Sources/ComposerToolbarViewModelTests.swift @@ -113,11 +113,11 @@ class ComposerToolbarViewModelTests: XCTestCase { XCTAssertEqual(viewModel.state.suggestions, suggestions) } - func testSuggestionTrigger() async { + func testSuggestionTrigger() async throws { + let deferred = deferFulfillment(wysiwygViewModel.$attributedContent) { $0.plainText == "#not_implemented_yay" } wysiwygViewModel.setMarkdownContent("@test") wysiwygViewModel.setMarkdownContent("#not_implemented_yay") - - await Task.yield() + try await deferred.fulfill() // The first one is nil because when initialised the view model is empty XCTAssertEqual(completionSuggestionServiceMock.setSuggestionTriggerReceivedInvocations, [nil, .init(type: .user, text: "test", range: .init(location: 0, length: 5)), nil]) diff --git a/UnitTests/Sources/TimelineViewModelTests.swift b/UnitTests/Sources/TimelineViewModelTests.swift index 9876a7cad1..22e63a25ab 100644 --- a/UnitTests/Sources/TimelineViewModelTests.swift +++ b/UnitTests/Sources/TimelineViewModelTests.swift @@ -256,39 +256,6 @@ class TimelineViewModelTests: XCTestCase { XCTAssertEqual(arguments?.type, .read) } - func testSendMoreReadReceipts() async throws { - // Given a room with only text items in the timeline that are all read. - let items = [TextRoomTimelineItem(eventID: "t1"), - TextRoomTimelineItem(eventID: "t2"), - TextRoomTimelineItem(eventID: "t3")] - let (viewModel, _, timelineProxy, timelineController) = readReceiptsConfiguration(with: items) - viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.last!.id)) - try await Task.sleep(for: .milliseconds(100)) - XCTAssertEqual(timelineProxy.sendReadReceiptForTypeCallsCount, 1) - var arguments = timelineProxy.sendReadReceiptForTypeReceivedArguments - XCTAssertEqual(arguments?.eventID, "t3") - XCTAssertEqual(arguments?.type, .read) - - // When sending a receipt for the first item in the timeline. - viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(items.first!.id)) - try await Task.sleep(for: .milliseconds(100)) - - // When a new message is received and marked as read. - let newMessage = TextRoomTimelineItem(eventID: "t4") - timelineController.timelineItems.append(newMessage) - timelineController.callbacks.send(.updatedTimelineItems(timelineItems: timelineController.timelineItems, isSwitchingTimelines: false)) - try await Task.sleep(for: .milliseconds(100)) - - viewModel.context.send(viewAction: .sendReadReceiptIfNeeded(newMessage.id)) - try await Task.sleep(for: .milliseconds(100)) - - // Then the request should be made. - XCTAssertEqual(timelineProxy.sendReadReceiptForTypeCallsCount, 3) - arguments = timelineProxy.sendReadReceiptForTypeReceivedArguments - XCTAssertEqual(arguments?.eventID, "t4") - XCTAssertEqual(arguments?.type, .read) - } - func testSendReadReceiptWithoutEvents() async throws { // Given a room with only virtual items. let items = [SeparatorRoomTimelineItem(timelineID: "v1"), diff --git a/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift b/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift index 24e482b76e..83f79dfa92 100644 --- a/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift +++ b/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift @@ -246,7 +246,7 @@ class UserSessionFlowCoordinatorTests: XCTestCase { private func process(route: AppRoute, expectedState: UserSessionFlowCoordinatorStateMachine.State) async throws { // Sometimes the state machine's state changes before the coordinators have updated the stack. - let delayedPublisher = userSessionFlowCoordinator.statePublisher.delay(for: .milliseconds(10), scheduler: DispatchQueue.main) + let delayedPublisher = userSessionFlowCoordinator.statePublisher.delay(for: .milliseconds(100), scheduler: DispatchQueue.main) let deferred = deferFulfillment(delayedPublisher) { $0 == expectedState } userSessionFlowCoordinator.handleAppRoute(route, animated: true) From 2184081bc5d2014e3a9d5b02d81892a06b730ae5 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Thu, 26 Sep 2024 18:07:50 +0100 Subject: [PATCH 009/114] Make sure the room header takes up as much space as possible (to hide the back button). (#3335) * Make sure the room header takes up as much space as possible (to hide the back button). * Ignore coverage on the developer options screen. --- ElementX/Sources/Screens/RoomScreen/View/RoomHeaderView.swift | 4 ++-- codecov.yml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomHeaderView.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomHeaderView.swift index e138e2fac6..68dc0a08c2 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/RoomHeaderView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomHeaderView.swift @@ -24,8 +24,8 @@ struct RoomHeaderView: View { .font(.compound.bodyLGSemibold) .accessibilityIdentifier(A11yIdentifiers.roomScreen.name) } - // Leading align whilst using the principal toolbar position. - .frame(maxWidth: .infinity, alignment: .leading) + // Take up as much space as possible, with a leading alignment for use in the principal toolbar position. + .frame(idealWidth: .greatestFiniteMagnitude, maxWidth: .infinity, alignment: .leading) } private var avatarImage: some View { diff --git a/codecov.yml b/codecov.yml index 7af5676dec..a13d2ab90a 100644 --- a/codecov.yml +++ b/codecov.yml @@ -14,6 +14,7 @@ ignore: - "ElementX/Sources/Vendor" - "ElementX/Sources/UITests" - "ElementX/Sources/UnitTests" + - "ElementX/Sources/Settings/DeveloperOptionsScreen" - "Tools" - "**/Mock*.swift" - "**/*Mock.swift" From 3b8da1c92d983caeb7cddea57fbb06d860d1d15a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 18:28:29 +0000 Subject: [PATCH 010/114] chore(deps): update dependency fastlane to v2.223.0 --- Gemfile.lock | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 95663e2412..c32fa8410e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -16,20 +16,20 @@ GEM artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.3.0) - aws-partitions (1.958.0) - aws-sdk-core (3.201.3) + aws-partitions (1.979.0) + aws-sdk-core (3.209.1) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-kms (1.94.0) + aws-sdk-core (~> 3, >= 3.207.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.156.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-s3 (1.166.0) + aws-sdk-core (~> 3, >= 3.207.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.9.0) + aws-sigv4 (1.10.0) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) base64 (0.2.0) @@ -45,7 +45,7 @@ GEM dotenv (2.8.1) emoji_regex (3.2.3) excon (0.111.0) - faraday (1.10.3) + faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -71,10 +71,10 @@ GEM faraday-patron (1.0.0) faraday-rack (1.0.0) faraday-retry (1.0.3) - faraday_middleware (1.2.0) + faraday_middleware (1.2.1) faraday (~> 1.0) fastimage (2.3.1) - fastlane (2.222.0) + fastlane (2.223.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -140,7 +140,7 @@ GEM google-apis-core (>= 0.11.0, < 2.a) google-apis-storage_v1 (0.31.0) google-apis-core (>= 0.11.0, < 2.a) - google-cloud-core (1.7.0) + google-cloud-core (1.7.1) google-cloud-env (>= 1.0, < 3.a) google-cloud-errors (~> 1.0) google-cloud-env (1.6.0) @@ -162,12 +162,12 @@ GEM signet (>= 0.16, < 2.a) highline (2.0.3) http-accept (1.7.0) - http-cookie (1.0.6) + http-cookie (1.0.7) domain_name (~> 0.5) httpclient (2.8.3) jmespath (1.6.2) json (2.7.2) - jwt (2.8.2) + jwt (2.9.1) base64 mime-types (3.5.2) mime-types-data (~> 3.2015) @@ -195,8 +195,7 @@ GEM mime-types (>= 1.16, < 4.0) netrc (~> 0.8) retriable (3.1.2) - rexml (3.2.9) - strscan + rexml (3.3.7) rouge (2.0.7) ruby2_keywords (0.0.5) rubyzip (2.3.2) @@ -210,7 +209,6 @@ GEM simctl (1.6.10) CFPropertyList naturally - strscan (3.1.0) terminal-notifier (2.0.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) @@ -220,18 +218,18 @@ GEM tty-spinner (0.9.3) tty-cursor (~> 0.7) uber (0.1.0) - unicode-display_width (2.5.0) + unicode-display_width (2.6.0) word_wrap (1.0.0) xcode-install (2.8.1) claide (>= 0.9.1) fastlane (>= 2.1.0, < 3.0.0) - xcodeproj (1.24.0) + xcodeproj (1.25.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.3.0) - rexml (~> 3.2.4) + rexml (>= 3.3.2, < 4.0) xcpretty (0.3.0) rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.1) From 4080ff199f821672e9eebfe909c1b92302184d71 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Fri, 27 Sep 2024 09:58:27 +0300 Subject: [PATCH 011/114] Have ElementCall always default to the speaker; prevent the lock button from ending the call - previously handled in 7818fa626ae0c8ccacca1ccaeffe6998359dd38d - seems the default behavior changed and we now need to manually configure the AudioSession --- .../Services/ElementCall/ElementCallService.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ElementX/Sources/Services/ElementCall/ElementCallService.swift b/ElementX/Sources/Services/ElementCall/ElementCallService.swift index 980e9b5f3e..9555d62e8b 100644 --- a/ElementX/Sources/Services/ElementCall/ElementCallService.swift +++ b/ElementX/Sources/Services/ElementCall/ElementCallService.swift @@ -103,6 +103,17 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe } catch { MXLog.error("Failed requesting start call action with error: \(error)") } + + do { + // Have ElementCall default to the speaker so that the lock button doesn't end the call. + // Could also use `overrideOutputAudioPort` but the documentation is clear about it: + // `Sessions using PlayAndRecord category that always want to prefer the built-in + // speaker output over the receiver, should use AVAudioSessionCategoryOptionDefaultToSpeaker instead.`. + try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .videoChat, options: [.defaultToSpeaker]) + try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation) + } catch { + MXLog.error("Failed setting up audio session with error: \(error)") + } } func tearDownCallSession() { From 605337b748a20e87cd89021202b837f936747fb8 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Fri, 27 Sep 2024 11:40:43 +0100 Subject: [PATCH 012/114] Log any failures when creating a call widget. (#3339) --- .../ElementCall/ElementCallWidgetDriver.swift | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/ElementX/Sources/Services/ElementCall/ElementCallWidgetDriver.swift b/ElementX/Sources/Services/ElementCall/ElementCallWidgetDriver.swift index 30a41e936f..70f80a09d9 100644 --- a/ElementX/Sources/Services/ElementCall/ElementCallWidgetDriver.swift +++ b/ElementX/Sources/Services/ElementCall/ElementCallWidgetDriver.swift @@ -71,28 +71,36 @@ class ElementCallWidgetDriver: WidgetCapabilitiesProvider, ElementCallWidgetDriv let useEncryption = (try? room.isEncrypted()) ?? false - guard let widgetSettings = try? newVirtualElementCallWidget(props: .init(elementCallUrl: baseURL.absoluteString, - widgetId: widgetID, - parentUrl: nil, - hideHeader: nil, - preload: nil, - fontScale: nil, - appPrompt: false, - skipLobby: true, - confineToRoom: true, - font: nil, - analyticsId: nil, - encryption: useEncryption ? .perParticipantKeys : .unencrypted)) else { + let widgetSettings: WidgetSettings + do { + widgetSettings = try newVirtualElementCallWidget(props: .init(elementCallUrl: baseURL.absoluteString, + widgetId: widgetID, + parentUrl: nil, + hideHeader: nil, + preload: nil, + fontScale: nil, + appPrompt: false, + skipLobby: true, + confineToRoom: true, + font: nil, + analyticsId: nil, + encryption: useEncryption ? .perParticipantKeys : .unencrypted)) + } catch { + MXLog.error("Failed to build widget settings: \(error)") return .failure(.failedBuildingWidgetSettings) } let languageTag = "\(Locale.current.language.languageCode ?? "en")-\(Locale.current.language.region ?? "US")" let theme = colorScheme == .light ? "light" : "dark" - guard let urlString = try? await generateWebviewUrl(widgetSettings: widgetSettings, room: room, - props: .init(clientId: clientID, - languageTag: languageTag, - theme: theme)) else { + let urlString: String + do { + urlString = try await generateWebviewUrl(widgetSettings: widgetSettings, room: room, + props: .init(clientId: clientID, + languageTag: languageTag, + theme: theme)) + } catch { + MXLog.error("Failed to generate web view URL: \(error)") return .failure(.failedBuildingCallURL) } @@ -100,7 +108,11 @@ class ElementCallWidgetDriver: WidgetCapabilitiesProvider, ElementCallWidgetDriv return .failure(.failedParsingCallURL) } - guard let widgetDriver = try? makeWidgetDriver(settings: widgetSettings) else { + let widgetDriver: WidgetDriverAndHandle + do { + widgetDriver = try makeWidgetDriver(settings: widgetSettings) + } catch { + MXLog.error("Failed to build widget driver: \(error)") return .failure(.failedBuildingWidgetDriver) } From df6d0a999fcf92c3d6bd5ad3bb708dcddb59b38c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 11:41:21 +0100 Subject: [PATCH 013/114] chore(deps): update dependency fastlane to v2.223.1 (#3340) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index c32fa8410e..8758696e1e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -74,7 +74,7 @@ GEM faraday_middleware (1.2.1) faraday (~> 1.0) fastimage (2.3.1) - fastlane (2.223.0) + fastlane (2.223.1) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) From 9d23dec2e9f9ea927031938e82df673acec0985d Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Fri, 27 Sep 2024 15:08:47 +0300 Subject: [PATCH 014/114] Tracing and integration test tweaks (#3336) * Disable image and document picker integration tests as they randomly fail to load and are flakey. * Delete any pre-existing log files * Various tracing tweaks and fixes: - delete the custom tracing log levels as we can't control their ouput - implement comparable on them - change default levels only if the new chosen level increases their verbosity * Make logging targets mandatory and fix their logging levels * Switch back to using the `run_tests` reset simulator flag as `fastlane snapshot reset_simulators` was too generic and slow * Switch all integration test taps to `tapCenter` (nee forceTap) after noticing missed taps on CI. * Make the logging file prefix explicit, let the main app not use one. * Rename tracing configuration `target` to `currentTarget` --- .github/workflows/integration-tests.yml | 6 +- .github/workflows/unit_tests.yml | 3 - .github/workflows/unit_tests_enterprise.yml | 3 - .../Sources/Application/AppCoordinator.swift | 2 +- .../Other/Extensions/XCUIElement.swift | 4 +- ElementX/Sources/Other/Logging/MXLog.swift | 19 ++--- .../Sources/Other/Logging/RustTracing.swift | 2 +- .../Other/Logging/TracingConfiguration.swift | 83 +++++++++---------- .../View/DeveloperOptionsScreen.swift | 27 +----- .../UITests/UITestsAppCoordinator.swift | 2 +- IntegrationTests/Sources/Common.swift | 21 +++-- IntegrationTests/Sources/UserFlowTests.swift | 34 ++++---- NSE/Sources/Other/NSELogger.swift | 2 +- UnitTests/Sources/LoggingTests.swift | 4 +- .../Sources/TracingConfigurationTests.swift | 39 +++++++-- fastlane/Fastfile | 15 ++-- 16 files changed, 127 insertions(+), 139 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index e446a5da9a..a884172491 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -30,8 +30,8 @@ jobs: run: source ci_scripts/ci_common.sh && setup_github_actions_environment - - name: Reset simulators - run: SNAPSHOT_FORCE_DELETE=1 bundle exec fastlane snapshot reset_simulators + - name: Delete old log files + run: find '/Users/Shared' -name 'console*' -delete - name: Run tests run: bundle exec fastlane integration_tests @@ -44,7 +44,7 @@ jobs: run: (grep ' TRACE ' /Users/Shared -qR) - name: Check logs don't contain private messages - run: (! grep 'Go down in flames' /Users/Shared -qR) + run: "! grep 'Go down in flames' /Users/Shared -R" - name: Zip results # for faster upload if: failure() diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 1d6206923e..63fa5e54ed 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -35,9 +35,6 @@ jobs: - name: SwiftFormat run: swiftformat --lint . - - - name: Reset simulators - run: SNAPSHOT_FORCE_DELETE=1 bundle exec fastlane snapshot reset_simulators - name: Run tests run: bundle exec fastlane unit_tests diff --git a/.github/workflows/unit_tests_enterprise.yml b/.github/workflows/unit_tests_enterprise.yml index 8df5b704c7..5bc8baac36 100644 --- a/.github/workflows/unit_tests_enterprise.yml +++ b/.github/workflows/unit_tests_enterprise.yml @@ -45,9 +45,6 @@ jobs: - name: SwiftFormat run: swiftformat --lint . - - name: Reset simulators - run: SNAPSHOT_FORCE_DELETE=1 bundle exec fastlane snapshot reset_simulators - - name: Run tests run: bundle exec fastlane unit_tests skip_previews:true diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index 18f366bea9..5652a91f2f 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -70,7 +70,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg let appSettings = appHooks.appSettingsHook.configure(AppSettings()) - MXLog.configure(logLevel: appSettings.logLevel) + MXLog.configure(currentTarget: "elementx", filePrefix: nil, logLevel: appSettings.logLevel) let appName = InfoPlistReader.main.bundleDisplayName let appVersion = InfoPlistReader.main.bundleShortVersionString diff --git a/ElementX/Sources/Other/Extensions/XCUIElement.swift b/ElementX/Sources/Other/Extensions/XCUIElement.swift index bb345b3c03..83c26a615f 100644 --- a/ElementX/Sources/Other/Extensions/XCUIElement.swift +++ b/ElementX/Sources/Other/Extensions/XCUIElement.swift @@ -9,7 +9,7 @@ import XCTest extension XCUIElement { func clearAndTypeText(_ text: String) { - forceTap() + tapCenter() guard let currentValue = value as? String else { XCTFail("Tried to clear and type text into a non string value") @@ -24,7 +24,7 @@ extension XCUIElement { } } - func forceTap() { + func tapCenter() { let coordinate: XCUICoordinate = coordinate(withNormalizedOffset: .init(dx: 0.5, dy: 0.5)) coordinate.tap() } diff --git a/ElementX/Sources/Other/Logging/MXLog.swift b/ElementX/Sources/Other/Logging/MXLog.swift index 652c257f00..974b02f594 100644 --- a/ElementX/Sources/Other/Logging/MXLog.swift +++ b/ElementX/Sources/Other/Logging/MXLog.swift @@ -22,21 +22,18 @@ enum MXLog { private static var didConfigureOnce = false private static var rootSpan: Span! - private static var target: String! + private static var currentTarget: String! - static func configure(target: String? = nil, + static func configure(currentTarget: String, + filePrefix: String?, logLevel: TracingConfiguration.LogLevel) { guard !didConfigureOnce else { return } - RustTracing.setup(configuration: .init(logLevel: logLevel, target: target)) + RustTracing.setup(configuration: .init(logLevel: logLevel, currentTarget: currentTarget, filePrefix: filePrefix)) - if let target { - self.target = target - } else { - self.target = Constants.target - } + self.currentTarget = currentTarget - rootSpan = Span(file: #file, line: #line, level: .info, target: self.target, name: "root") + rootSpan = Span(file: #file, line: #line, level: .info, target: self.currentTarget, name: "root") rootSpan.enter() didConfigureOnce = true @@ -138,7 +135,7 @@ enum MXLog { rootSpan.enter() } - return Span(file: file, line: UInt32(line), level: level, target: target, name: name) + return Span(file: file, line: UInt32(line), level: level, target: currentTarget, name: name) } // periphery:ignore:parameters function,column,context @@ -157,6 +154,6 @@ enum MXLog { rootSpan.enter() } - logEvent(file: (file as NSString).lastPathComponent, line: UInt32(line), level: level, target: target, message: "\(message)") + logEvent(file: (file as NSString).lastPathComponent, line: UInt32(line), level: level, target: currentTarget, message: "\(message)") } } diff --git a/ElementX/Sources/Other/Logging/RustTracing.swift b/ElementX/Sources/Other/Logging/RustTracing.swift index 8e4c0b9b32..35f55000b4 100644 --- a/ElementX/Sources/Other/Logging/RustTracing.swift +++ b/ElementX/Sources/Other/Logging/RustTracing.swift @@ -32,7 +32,7 @@ enum RustTracing { // Log everything on integration tests to check whether // the logs contain any sensitive data. See `UserFlowTests.swift` let filter = if ProcessInfo.isRunningIntegrationTests { - TracingConfiguration(logLevel: .trace, target: nil).filter + TracingConfiguration(logLevel: .trace, currentTarget: "integrationtests", filePrefix: nil).filter } else { configuration.filter } diff --git a/ElementX/Sources/Other/Logging/TracingConfiguration.swift b/ElementX/Sources/Other/Logging/TracingConfiguration.swift index 3ff5250de0..a8d0a061c2 100644 --- a/ElementX/Sources/Other/Logging/TracingConfiguration.swift +++ b/ElementX/Sources/Other/Logging/TracingConfiguration.swift @@ -11,9 +11,8 @@ import Collections // We can filter by level, crate and even file. See more details here: // https://docs.rs/tracing-subscriber/0.2.7/tracing_subscriber/filter/struct.EnvFilter.html#examples struct TracingConfiguration { - enum LogLevel: Codable, Hashable { + enum LogLevel: String, Codable, Hashable, Comparable { case error, warn, info, debug, trace - case custom(String) var title: String { switch self { @@ -27,34 +26,32 @@ struct TracingConfiguration { return "Debug" case .trace: return "Trace" - case .custom: - return "Custom" } } - fileprivate var rawValue: String { - switch self { - case .error: - return "error" - case .warn: - return "warn" - case .info: - return "info" - case .debug: - return "debug" - case .trace: - return "trace" - case .custom(let filter): - return filter + static func < (lhs: TracingConfiguration.LogLevel, rhs: TracingConfiguration.LogLevel) -> Bool { + switch (lhs, rhs) { + case (.error, _): + true + case (.warn, .error): + false + case (.warn, _): + true + case (.info, .error), (.info, .warn): + false + case (.info, _): + true + case (.debug, .error), (.debug, .warn), (.debug, .info): + false + case (.debug, _): + true + case (.trace, _): + false } } } enum Target: String { - case common = "" - - case elementx - case hyper, matrix_sdk_ffi, matrix_sdk_crypto case matrix_sdk_client = "matrix_sdk::client" @@ -66,9 +63,8 @@ struct TracingConfiguration { case matrix_sdk_ui_timeline = "matrix_sdk_ui::timeline" } + // The `common` target is excluded because 3rd-party crates might end up logging user data. static let targets: OrderedDictionary = [ - .common: .info, // Never set this lower than info - 3rd-party crates may start logging user data. - .elementx: .info, .hyper: .warn, .matrix_sdk_ffi: .info, .matrix_sdk_client: .trace, @@ -92,33 +88,30 @@ struct TracingConfiguration { /// - Parameter logLevel: the desired log level /// - Parameter target: the name of the target being configured /// - Returns: a custom tracing configuration - init(logLevel: LogLevel, target: String?) { - fileName = if let target { - "\(RustTracing.filePrefix)-\(target)" + init(logLevel: LogLevel, currentTarget: String, filePrefix: String?) { + fileName = if let filePrefix { + "\(RustTracing.filePrefix)-\(filePrefix)" } else { RustTracing.filePrefix } - - if case let .custom(filter) = logLevel { - self.filter = filter - return - } - + let overrides = Self.targets.keys.reduce(into: [Target: LogLevel]()) { partialResult, target in // Keep the defaults here - let ignoredTargets: [Target] = [.common, // Never remove common from the ignored targets (see above for more info). - .hyper, - .matrix_sdk_ffi, - .matrix_sdk_oidc, - .matrix_sdk_client, - .matrix_sdk_crypto, - .matrix_sdk_crypto_account, - .matrix_sdk_http_client] + let ignoredTargets: [Target] = [.hyper] + if ignoredTargets.contains(target) { return } - partialResult[target] = logLevel + guard let defaultTargetLogLevel = Self.targets[target] else { + return + } + + // Only change the targets that have default values + // smaller than the desired log level + if defaultTargetLogLevel < logLevel { + partialResult[target] = logLevel + } } var newTargets = Self.targets @@ -126,7 +119,7 @@ struct TracingConfiguration { newTargets.updateValue(logLevel, forKey: target) } - let components = newTargets.map { (target: Target, logLevel: LogLevel) in + var components = newTargets.map { (target: Target, logLevel: LogLevel) in guard !target.rawValue.isEmpty else { return logLevel.rawValue } @@ -134,6 +127,10 @@ struct TracingConfiguration { return "\(target.rawValue)=\(logLevel.rawValue)" } + // With `common` not being used we manually need to specify the log + // level for passed in targets + components.append("\(currentTarget)=\(logLevel.rawValue)") + filter = components.joined(separator: ",") } } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift index 24152f5beb..30f6e8ec87 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -135,18 +135,6 @@ struct DeveloperOptionsScreen: View { private struct LogLevelConfigurationView: View { @Binding var logLevel: TracingConfiguration.LogLevel - @State private var customTracingConfiguration: String - - init(logLevel: Binding) { - _logLevel = logLevel - - if case .custom(let configuration) = logLevel.wrappedValue { - customTracingConfiguration = configuration - } else { - customTracingConfiguration = TracingConfiguration(logLevel: .info, target: nil).filter - } - } - var body: some View { Picker(selection: $logLevel) { ForEach(logLevels, id: \.self) { logLevel in @@ -156,24 +144,11 @@ private struct LogLevelConfigurationView: View { Text("Log level") Text("Requires app reboot") } - - if case .custom = logLevel { - TextEditor(text: $customTracingConfiguration) - .textInputAutocapitalization(.never) - .autocorrectionDisabled() - .onChange(of: customTracingConfiguration) { newValue in - logLevel = .custom(newValue) - } - } } /// Allows the picker to work with associated values private var logLevels: [TracingConfiguration.LogLevel] { - if case let .custom(filter) = logLevel { - return [.error, .warn, .info, .debug, .trace, .custom(filter)] - } else { - return [.error, .warn, .info, .debug, .trace, .custom("")] - } + [.error, .warn, .info, .debug, .trace] } } diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index 2af7f39bcc..030e58bc37 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -30,7 +30,7 @@ class UITestsAppCoordinator: AppCoordinatorProtocol, SecureWindowManagerDelegate windowManager.delegate = self - MXLog.configure(logLevel: .debug) + MXLog.configure(currentTarget: "uitests", filePrefix: nil, logLevel: .debug) ServiceLocator.shared.register(userIndicatorController: UserIndicatorController()) diff --git a/IntegrationTests/Sources/Common.swift b/IntegrationTests/Sources/Common.swift index 51c334cd34..e79483605a 100644 --- a/IntegrationTests/Sources/Common.swift +++ b/IntegrationTests/Sources/Common.swift @@ -12,12 +12,11 @@ extension XCUIApplication { let getStartedButton = buttons[A11yIdentifiers.authenticationStartScreen.signIn] XCTAssertTrue(getStartedButton.waitForExistence(timeout: 10.0)) - getStartedButton.tap() + getStartedButton.tapCenter() - // Get started is network bound, wait for the change homeserver button for longer let changeHomeserverButton = buttons[A11yIdentifiers.serverConfirmationScreen.changeServer] - XCTAssertTrue(changeHomeserverButton.waitForExistence(timeout: 30.0)) - changeHomeserverButton.tap() + XCTAssertTrue(changeHomeserverButton.waitForExistence(timeout: 10.0)) + changeHomeserverButton.tapCenter() let homeserverTextField = textFields[A11yIdentifiers.changeServerScreen.server] XCTAssertTrue(homeserverTextField.waitForExistence(timeout: 10.0)) @@ -26,7 +25,7 @@ extension XCUIApplication { let confirmButton = buttons[A11yIdentifiers.changeServerScreen.continue] XCTAssertTrue(confirmButton.waitForExistence(timeout: 10.0)) - confirmButton.tap() + confirmButton.tapCenter() // Wait for server confirmation to finish let doesNotExistPredicate = NSPredicate(format: "exists == 0") @@ -35,7 +34,7 @@ extension XCUIApplication { let continueButton = buttons[A11yIdentifiers.serverConfirmationScreen.continue] XCTAssertTrue(continueButton.waitForExistence(timeout: 30.0)) - continueButton.tap() + continueButton.tapCenter() let usernameTextField = textFields[A11yIdentifiers.loginScreen.emailUsername] XCTAssertTrue(usernameTextField.waitForExistence(timeout: 10.0)) @@ -51,7 +50,7 @@ extension XCUIApplication { XCTAssertTrue(nextButton.waitForExistence(timeout: 10.0)) XCTAssertTrue(nextButton.isEnabled) - nextButton.tap() + nextButton.tapCenter() // Wait for login to finish currentTestCase.expectation(for: doesNotExistPredicate, evaluatedWith: usernameTextField) @@ -63,7 +62,7 @@ extension XCUIApplication { // Tapping the sheet button while animating upwards fails. Wait for it to settle sleep(1) - savePasswordButton.tap() + savePasswordButton.tapCenter() } // Wait for the home screen to become visible. @@ -80,17 +79,17 @@ extension XCUIApplication { let profileButton = buttons[A11yIdentifiers.homeScreen.userAvatar] // `Failed to scroll to visible (by AX action) Button` https://stackoverflow.com/a/33534187/730924 - profileButton.forceTap() + profileButton.tapCenter() // Logout let logoutButton = buttons[A11yIdentifiers.settingsScreen.logout] XCTAssertTrue(logoutButton.waitForExistence(timeout: 10.0)) - logoutButton.tap() + logoutButton.tapCenter() // Confirm logout let alertLogoutButton = alerts.firstMatch.buttons["Sign out"] XCTAssertTrue(alertLogoutButton.waitForExistence(timeout: 10.0)) - alertLogoutButton.tap() + alertLogoutButton.tapCenter() // Check that we're back on the login screen let getStartedButton = buttons[A11yIdentifiers.authenticationStartScreen.signIn] diff --git a/IntegrationTests/Sources/UserFlowTests.swift b/IntegrationTests/Sources/UserFlowTests.swift index a5f78038d1..bd0be27d6c 100644 --- a/IntegrationTests/Sources/UserFlowTests.swift +++ b/IntegrationTests/Sources/UserFlowTests.swift @@ -37,13 +37,13 @@ class UserFlowTests: XCTestCase { // And open it let firstRoom = app.buttons.matching(NSPredicate(format: "identifier CONTAINS %@", Self.integrationTestsRoomName)).firstMatch XCTAssertTrue(firstRoom.waitForExistence(timeout: 10.0)) - firstRoom.tap() + firstRoom.tapCenter() sendMessages() - checkPhotoSharing() - - checkDocumentSharing() + // Intentionally disabled as they're super flakey on iOS 18 simulators + // checkPhotoSharing() + // checkDocumentSharing() checkLocationSharing() @@ -57,7 +57,7 @@ class UserFlowTests: XCTestCase { // Cancel initial the room search let searchCancelButton = app.buttons["Cancel"].firstMatch XCTAssertTrue(searchCancelButton.waitForExistence(timeout: 10.0)) - searchCancelButton.forceTap() + searchCancelButton.tapCenter() } private func sendMessages() { @@ -67,7 +67,7 @@ class UserFlowTests: XCTestCase { var sendButton = app.buttons[A11yIdentifiers.roomScreen.sendButton].firstMatch XCTAssertTrue(sendButton.waitForExistence(timeout: 10.0)) - sendButton.tap() + sendButton.tapCenter() sleep(10) // Wait for the message to be sent @@ -81,12 +81,12 @@ class UserFlowTests: XCTestCase { sendButton = app.buttons[A11yIdentifiers.roomScreen.sendButton].firstMatch XCTAssertTrue(sendButton.waitForExistence(timeout: 10.0)) - sendButton.tap() + sendButton.tapCenter() sleep(5) // Wait for the message to be sent // Close the formatting options - app.buttons[A11yIdentifiers.roomScreen.composerToolbar.closeFormattingOptions].tap() + app.buttons[A11yIdentifiers.roomScreen.composerToolbar.closeFormattingOptions].tapCenter() } private func checkPhotoSharing() { @@ -98,7 +98,7 @@ class UserFlowTests: XCTestCase { // Tap on the second image. First one is always broken on simulators. let secondImage = app.scrollViews.images.element(boundBy: 1) XCTAssertTrue(secondImage.waitForExistence(timeout: 20.0)) // Photo library takes a bit to load - secondImage.tap() + secondImage.tapCenter() // Wait for the image to be processed and the new screen to appear sleep(10) @@ -129,7 +129,7 @@ class UserFlowTests: XCTestCase { // Handle map loading errors (missing credentials) let alertOkButton = app.alerts.firstMatch.buttons["OK"].firstMatch if alertOkButton.waitForExistence(timeout: 10.0) { - alertOkButton.tap() + alertOkButton.tapCenter() } allowLocationPermissionOnce() @@ -141,7 +141,7 @@ class UserFlowTests: XCTestCase { let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") let notificationAlertAllowButton = springboard.buttons["Allow Once"].firstMatch if notificationAlertAllowButton.waitForExistence(timeout: 10.0) { - notificationAlertAllowButton.tap() + notificationAlertAllowButton.tapCenter() } } @@ -178,7 +178,7 @@ class UserFlowTests: XCTestCase { // Open the room details let roomHeader = app.staticTexts[A11yIdentifiers.roomScreen.name] XCTAssertTrue(roomHeader.waitForExistence(timeout: 10.0)) - roomHeader.tap() + roomHeader.tapCenter() // Open the room member details tapOnButton(A11yIdentifiers.roomDetailsScreen.people) @@ -186,7 +186,7 @@ class UserFlowTests: XCTestCase { // Open the first member's details. Loading members for big rooms can take a while. let firstRoomMember = app.scrollViews.buttons.firstMatch XCTAssertTrue(firstRoomMember.waitForExistence(timeout: 1000.0)) - firstRoomMember.tap() + firstRoomMember.tapCenter() // Go back to the room member details tapOnBackButton("People") @@ -206,7 +206,7 @@ class UserFlowTests: XCTestCase { let profileButton = app.buttons[A11yIdentifiers.homeScreen.userAvatar] // `Failed to scroll to visible (by AX action) Button` https://stackoverflow.com/a/33534187/730924 - profileButton.forceTap() + profileButton.tapCenter() // Open analytics tapOnButton(A11yIdentifiers.settingsScreen.analytics) @@ -233,7 +233,7 @@ class UserFlowTests: XCTestCase { private func tapOnButton(_ identifier: String, waitForDisappearance: Bool = false) { let button = app.buttons[identifier] XCTAssertTrue(button.waitForExistence(timeout: 10.0)) - button.tap() + button.tapCenter() if waitForDisappearance { let doesNotExistPredicate = NSPredicate(format: "exists == 0") @@ -245,7 +245,7 @@ class UserFlowTests: XCTestCase { private func tapOnMenu(_ identifier: String) { let button = app.buttons[identifier] XCTAssertTrue(button.waitForExistence(timeout: 10.0)) - button.forceTap() + button.tapCenter() } /// Taps on a back button that the system configured with a label but no identifier. @@ -255,6 +255,6 @@ class UserFlowTests: XCTestCase { private func tapOnBackButton(_ label: String = "Back") { let button = app.buttons.matching(NSPredicate(format: "label == %@ && identifier == ''", label)).firstMatch XCTAssertTrue(button.waitForExistence(timeout: 10.0)) - button.tap() + button.tapCenter() } } diff --git a/NSE/Sources/Other/NSELogger.swift b/NSE/Sources/Other/NSELogger.swift index c0f7cc4f28..985038136a 100644 --- a/NSE/Sources/Other/NSELogger.swift +++ b/NSE/Sources/Other/NSELogger.swift @@ -72,7 +72,7 @@ enum NSELogger { } isConfigured = true - MXLog.configure(target: "nse", logLevel: logLevel) + MXLog.configure(currentTarget: "nse", filePrefix: "nse", logLevel: logLevel) } static func logMemory(with tag: String) { diff --git a/UnitTests/Sources/LoggingTests.swift b/UnitTests/Sources/LoggingTests.swift index 56d34ed35c..df701300a7 100644 --- a/UnitTests/Sources/LoggingTests.swift +++ b/UnitTests/Sources/LoggingTests.swift @@ -22,7 +22,7 @@ class LoggingTests: XCTestCase { let target = "tests" XCTAssertTrue(RustTracing.logFiles.isEmpty) - MXLog.configure(target: target, logLevel: .info) + MXLog.configure(currentTarget: target, filePrefix: target, logLevel: .info) // There is something weird with Rust logging where the file writing handle doesn't // notice that the file it is writing to was deleted, so we can't run these checks @@ -168,7 +168,7 @@ class LoggingTests: XCTestCase { content: .init(body: "FileString", source: nil, thumbnailSource: nil, contentType: nil)) // When logging that value - MXLog.configure(logLevel: .info) + MXLog.configure(currentTarget: "tests", filePrefix: nil, logLevel: .info) MXLog.info(textMessage) MXLog.info(noticeMessage) diff --git a/UnitTests/Sources/TracingConfigurationTests.swift b/UnitTests/Sources/TracingConfigurationTests.swift index 864c8898fe..2150be85be 100644 --- a/UnitTests/Sources/TracingConfigurationTests.swift +++ b/UnitTests/Sources/TracingConfigurationTests.swift @@ -10,14 +10,35 @@ import XCTest @testable import ElementX class TracingConfigurationTests: XCTestCase { - func testConfiguration() { - let configuration = TracingConfiguration(logLevel: .trace, target: nil) - - let filterComponents = configuration.filter.components(separatedBy: ",") - XCTAssertEqual(filterComponents.first, "info") - XCTAssertTrue(filterComponents.contains("matrix_sdk_base::sliding_sync=trace")) - XCTAssertTrue(filterComponents.contains("matrix_sdk::http_client=debug")) - XCTAssertTrue(filterComponents.contains("matrix_sdk_crypto=debug")) - XCTAssertTrue(filterComponents.contains("hyper=warn")) + func testConfiguration() { // swiftlint:disable line_length + var filter = TracingConfiguration(logLevel: .error, currentTarget: "tests", filePrefix: nil).filter + + XCTAssertEqual(filter, "hyper=warn,matrix_sdk_ffi=info,matrix_sdk::client=trace,matrix_sdk_crypto=debug,matrix_sdk_crypto::olm::account=trace,matrix_sdk::oidc=trace,matrix_sdk::http_client=debug,matrix_sdk::sliding_sync=info,matrix_sdk_base::sliding_sync=info,matrix_sdk_ui::timeline=info,tests=error") + + filter = TracingConfiguration(logLevel: .warn, currentTarget: "tests", filePrefix: nil).filter + + XCTAssertEqual(filter, "hyper=warn,matrix_sdk_ffi=info,matrix_sdk::client=trace,matrix_sdk_crypto=debug,matrix_sdk_crypto::olm::account=trace,matrix_sdk::oidc=trace,matrix_sdk::http_client=debug,matrix_sdk::sliding_sync=info,matrix_sdk_base::sliding_sync=info,matrix_sdk_ui::timeline=info,tests=warn") + + filter = TracingConfiguration(logLevel: .info, currentTarget: "tests", filePrefix: nil).filter + + XCTAssertEqual(filter, "hyper=warn,matrix_sdk_ffi=info,matrix_sdk::client=trace,matrix_sdk_crypto=debug,matrix_sdk_crypto::olm::account=trace,matrix_sdk::oidc=trace,matrix_sdk::http_client=debug,matrix_sdk::sliding_sync=info,matrix_sdk_base::sliding_sync=info,matrix_sdk_ui::timeline=info,tests=info") + + filter = TracingConfiguration(logLevel: .debug, currentTarget: "tests", filePrefix: nil).filter + + XCTAssertEqual(filter, "hyper=warn,matrix_sdk_ffi=debug,matrix_sdk::client=trace,matrix_sdk_crypto=debug,matrix_sdk_crypto::olm::account=trace,matrix_sdk::oidc=trace,matrix_sdk::http_client=debug,matrix_sdk::sliding_sync=debug,matrix_sdk_base::sliding_sync=debug,matrix_sdk_ui::timeline=debug,tests=debug") + + filter = TracingConfiguration(logLevel: .trace, currentTarget: "tests", filePrefix: nil).filter + + XCTAssertEqual(filter, "hyper=warn,matrix_sdk_ffi=trace,matrix_sdk::client=trace,matrix_sdk_crypto=trace,matrix_sdk_crypto::olm::account=trace,matrix_sdk::oidc=trace,matrix_sdk::http_client=trace,matrix_sdk::sliding_sync=trace,matrix_sdk_base::sliding_sync=trace,matrix_sdk_ui::timeline=trace,tests=trace") + } // swiftlint:enable line_length + + func testLevelOrdering() { + var logLevels: [TracingConfiguration.LogLevel] = [.info, .error, .trace, .debug, .warn] + + XCTAssertEqual(logLevels.sorted(), [.error, .warn, .info, .debug, .trace]) + + logLevels = [.warn, .error, .debug, .trace, .info, .error] + + XCTAssertEqual(logLevels.sorted(), [.error, .error, .warn, .info, .debug, .trace]) } } diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 05ea80f629..881b9cf950 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -80,12 +80,15 @@ lane :alpha do end lane :unit_tests do |options| + reset_simulator = ENV.key?('CI') + run_tests( scheme: "UnitTests", destination: "platform=iOS Simulator,name=iPhone 16,OS=18.0", ensure_devices_found: true, result_bundle: true, number_of_retries: 3, + reset_simulator: reset_simulator ) if !options[:skip_previews] @@ -95,6 +98,7 @@ lane :unit_tests do |options| ensure_devices_found: true, result_bundle: true, number_of_retries: 3, + reset_simulator: reset_simulator ) end @@ -102,10 +106,6 @@ lane :unit_tests do |options| end lane :ui_tests do |options| - # Use a fresh simulator state to ensure hardware keyboard isn't attached. - # Not necessary when running on GitHub. - # reset_simulator_contents() - create_simulator_if_necessary( name: "iPhone 16", type: "com.apple.CoreSimulator.SimDeviceType.iPhone-16", @@ -124,6 +124,8 @@ lane :ui_tests do |options| test_to_run = nil end + reset_simulator = ENV.key?('CI') + run_tests( scheme: "UITests", devices: ["iPhone 16", "iPad (10th generation)"], @@ -132,6 +134,7 @@ lane :ui_tests do |options| result_bundle: true, only_testing: test_to_run, number_of_retries: 3, + reset_simulator: reset_simulator ) end @@ -145,12 +148,14 @@ lane :integration_tests do runtime: "com.apple.CoreSimulator.SimRuntime.iOS-18-0" ) + reset_simulator = ENV.key?('CI') + run_tests( scheme: "IntegrationTests", destination: "platform=iOS Simulator,name=iPhone 16 Pro,OS=18.0", ensure_devices_found: true, result_bundle: true, - reset_simulator: true + reset_simulator: reset_simulator ) end From 8a3994016d5eab4cfeb5885673f379a4c053d8c9 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Fri, 27 Sep 2024 15:23:20 +0300 Subject: [PATCH 015/114] Allow focusing the different avatars making up a DM details cluster separately. (#3341) --- .../SwiftUI/Views/AvatarHeaderView.swift | 24 +++++++++---------- .../SwiftUI/Views/LoadableAvatarImage.swift | 23 ++++++++++++++++-- .../Other/SwiftUI/Views/RoomAvatarImage.swift | 14 +++++++---- .../RoomDetailsScreenModels.swift | 2 +- .../RoomDetailsScreenViewModel.swift | 12 ++++------ .../View/RoomDetailsScreen.swift | 8 +++---- .../RoomMemberDetailsScreenModels.swift | 2 +- .../RoomMemberDetailsScreenViewModel.swift | 12 ++++------ .../View/RoomMemberDetailsScreen.swift | 4 ++-- .../UserProfileScreenModels.swift | 2 +- .../UserProfileScreenViewModel.swift | 9 ++++--- .../View/UserProfileScreen.swift | 4 ++-- 12 files changed, 65 insertions(+), 51 deletions(-) diff --git a/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift b/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift index 4b63f20cae..4705b76a13 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift @@ -26,13 +26,13 @@ struct AvatarHeaderView: View { private let avatarSize: AvatarSize private let mediaProvider: MediaProviderProtocol? - private var onAvatarTap: (() -> Void)? + private var onAvatarTap: ((URL) -> Void)? @ViewBuilder private var footer: () -> Footer init(room: RoomDetails, avatarSize: AvatarSize, mediaProvider: MediaProviderProtocol? = nil, - onAvatarTap: (() -> Void)? = nil, + onAvatarTap: ((URL) -> Void)? = nil, @ViewBuilder footer: @escaping () -> Footer) { avatarInfo = .room(room.avatar) title = room.name ?? room.id @@ -54,7 +54,7 @@ struct AvatarHeaderView: View { init(accountOwner: RoomMemberDetails, dmRecipient: RoomMemberDetails, mediaProvider: MediaProviderProtocol? = nil, - onAvatarTap: (() -> Void)? = nil, + onAvatarTap: ((URL) -> Void)? = nil, @ViewBuilder footer: @escaping () -> Footer) { let dmRecipientProfile = UserProfileProxy(member: dmRecipient) avatarInfo = .room(.heroes([dmRecipientProfile, UserProfileProxy(member: accountOwner)])) @@ -72,7 +72,7 @@ struct AvatarHeaderView: View { init(member: RoomMemberDetails, avatarSize: AvatarSize, mediaProvider: MediaProviderProtocol? = nil, - onAvatarTap: (() -> Void)? = nil, + onAvatarTap: ((URL) -> Void)? = nil, @ViewBuilder footer: @escaping () -> Footer) { let profile = UserProfileProxy(member: member) @@ -86,7 +86,7 @@ struct AvatarHeaderView: View { init(user: UserProfileProxy, avatarSize: AvatarSize, mediaProvider: MediaProviderProtocol? = nil, - onAvatarTap: (() -> Void)? = nil, + onAvatarTap: ((URL) -> Void)? = nil, @ViewBuilder footer: @escaping () -> Footer) { avatarInfo = .user(user) title = user.displayName ?? user.userID @@ -128,24 +128,22 @@ struct AvatarHeaderView: View { case .room(let roomAvatar): RoomAvatarImage(avatar: roomAvatar, avatarSize: avatarSize, - mediaProvider: mediaProvider) + mediaProvider: mediaProvider, + onAvatarTap: onAvatarTap) + case .user(let userProfile): LoadableAvatarImage(url: userProfile.avatarURL, name: userProfile.displayName, contentID: userProfile.userID, avatarSize: avatarSize, - mediaProvider: mediaProvider) + mediaProvider: mediaProvider, + onTap: onAvatarTap) } } var body: some View { VStack(spacing: 8.0) { - Button { - onAvatarTap?() - } label: { - avatar - } - .buttonStyle(.borderless) // Add a button style to stop the whole row being tappable. + avatar Spacer() .frame(height: 9) diff --git a/ElementX/Sources/Other/SwiftUI/Views/LoadableAvatarImage.swift b/ElementX/Sources/Other/SwiftUI/Views/LoadableAvatarImage.swift index f311b321f8..2c35c9a5e9 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/LoadableAvatarImage.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/LoadableAvatarImage.swift @@ -13,20 +13,39 @@ struct LoadableAvatarImage: View { private let contentID: String? private let avatarSize: AvatarSize private let mediaProvider: MediaProviderProtocol? + private let onTap: ((URL) -> Void)? @ScaledMetric private var frameSize: CGFloat - init(url: URL?, name: String?, contentID: String?, avatarSize: AvatarSize, mediaProvider: MediaProviderProtocol?) { + init(url: URL?, name: String?, + contentID: String?, + avatarSize: AvatarSize, + mediaProvider: MediaProviderProtocol?, + onTap: ((URL) -> Void)? = nil) { self.url = url self.name = name self.contentID = contentID self.avatarSize = avatarSize self.mediaProvider = mediaProvider + self.onTap = onTap _frameSize = ScaledMetric(wrappedValue: avatarSize.value) } var body: some View { + if let onTap, let url { + Button { + onTap(url) + } label: { + clippedAvatar + } + .buttonStyle(.borderless) // Add a button style to stop the whole row being tappable. + } else { + clippedAvatar + } + } + + private var clippedAvatar: some View { avatar .frame(width: frameSize, height: frameSize) .background(Color.compound.bgCanvasDefault) @@ -34,7 +53,7 @@ struct LoadableAvatarImage: View { } @ViewBuilder - var avatar: some View { + private var avatar: some View { if let url { LoadableImage(url: url, mediaType: .avatar, diff --git a/ElementX/Sources/Other/SwiftUI/Views/RoomAvatarImage.swift b/ElementX/Sources/Other/SwiftUI/Views/RoomAvatarImage.swift index c7f77b116f..78d4c96e1b 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/RoomAvatarImage.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/RoomAvatarImage.swift @@ -25,6 +25,8 @@ struct RoomAvatarImage: View { let avatarSize: AvatarSize let mediaProvider: MediaProviderProtocol? + private(set) var onAvatarTap: ((URL) -> Void)? + var body: some View { switch avatar { case .room(let id, let name, let avatarURL): @@ -32,7 +34,8 @@ struct RoomAvatarImage: View { name: name, contentID: id, avatarSize: avatarSize, - mediaProvider: mediaProvider) + mediaProvider: mediaProvider, + onTap: onAvatarTap) case .heroes(let users): // We will expand upon this with more stack sizes in the future. if users.count == 0 { @@ -45,14 +48,16 @@ struct RoomAvatarImage: View { name: users[0].displayName, contentID: users[0].userID, avatarSize: avatarSize, - mediaProvider: mediaProvider) + mediaProvider: mediaProvider, + onTap: onAvatarTap) .scaledFrame(size: clusterSize, alignment: .topTrailing) LoadableAvatarImage(url: users[1].avatarURL, name: users[1].displayName, contentID: users[1].userID, avatarSize: avatarSize, - mediaProvider: mediaProvider) + mediaProvider: mediaProvider, + onTap: onAvatarTap) .mask { Rectangle() .fill(Color.white) @@ -74,7 +79,8 @@ struct RoomAvatarImage: View { name: users[0].displayName, contentID: users[0].userID, avatarSize: avatarSize, - mediaProvider: mediaProvider) + mediaProvider: mediaProvider, + onTap: onAvatarTap) } } } diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift index 77a328956f..72ff876c93 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift @@ -183,7 +183,7 @@ enum RoomDetailsScreenViewAction { case unignoreConfirmed case processTapNotifications case processToggleMuteNotifications - case displayAvatar + case displayAvatar(URL) case processTapPolls case toggleFavourite(isFavourite: Bool) case processTapRolesAndPermissions diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift index 79e83484e2..b435eeb232 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift @@ -150,8 +150,8 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr } case .processToggleMuteNotifications: Task { await toggleMuteNotifications() } - case .displayAvatar: - displayFullScreenAvatar() + case .displayAvatar(let url): + displayFullScreenAvatar(url) case .processTapPolls: actionsSubject.send(.requestPollsHistoryPresentation) case .toggleFavourite(let isFavourite): @@ -346,11 +346,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr } } - private func displayFullScreenAvatar() { - guard let avatarURL = roomProxy.avatarURL else { - return - } - + private func displayFullScreenAvatar(_ url: URL) { let loadingIndicatorIdentifier = "roomAvatarLoadingIndicator" userIndicatorController.submitIndicator(UserIndicator(id: loadingIndicatorIdentifier, type: .modal, title: L10n.commonLoading, persistent: true)) @@ -360,7 +356,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr } // We don't actually know the mime type here, assume it's an image. - if case let .success(file) = await mediaProvider.loadFileFromSource(.init(url: avatarURL, mimeType: "image/jpeg")) { + if case let .success(file) = await mediaProvider.loadFileFromSource(.init(url: url, mimeType: "image/jpeg")) { state.bindings.mediaPreviewItem = MediaPreviewItem(file: file, title: roomProxy.roomTitle) } } diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift b/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift index 73a8446f98..3b682e8b26 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift @@ -65,8 +65,8 @@ struct RoomDetailsScreen: View { private var normalRoomHeaderSection: some View { AvatarHeaderView(room: context.viewState.details, avatarSize: .room(on: .details), - mediaProvider: context.mediaProvider) { - context.send(viewAction: .displayAvatar) + mediaProvider: context.mediaProvider) { url in + context.send(viewAction: .displayAvatar(url)) } footer: { if !context.viewState.shortcuts.isEmpty { headerSectionShortcuts @@ -78,8 +78,8 @@ struct RoomDetailsScreen: View { private func dmHeaderSection(accountOwner: RoomMemberDetails, recipient: RoomMemberDetails) -> some View { AvatarHeaderView(accountOwner: accountOwner, dmRecipient: recipient, - mediaProvider: context.mediaProvider) { - context.send(viewAction: .displayAvatar) + mediaProvider: context.mediaProvider) { url in + context.send(viewAction: .displayAvatar(url)) } footer: { if !context.viewState.shortcuts.isEmpty { headerSectionShortcuts diff --git a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenModels.swift b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenModels.swift index fb444c2a10..0b33b653c8 100644 --- a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenModels.swift +++ b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenModels.swift @@ -74,7 +74,7 @@ enum RoomMemberDetailsScreenViewAction { case showIgnoreAlert case ignoreConfirmed case unignoreConfirmed - case displayAvatar + case displayAvatar(URL) case openDirectChat case startCall(roomID: String) } diff --git a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModel.swift b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModel.swift index 6249d7da6c..1bd2dc81d9 100644 --- a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModel.swift @@ -84,8 +84,8 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro Task { await ignoreUser() } case .unignoreConfirmed: Task { await unignoreUser() } - case .displayAvatar: - Task { await displayFullScreenAvatar() } + case .displayAvatar(let url): + Task { await displayFullScreenAvatar(url) } case .openDirectChat: Task { await openDirectChat() } case .startCall(let roomID): @@ -143,21 +143,17 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro } } - private func displayFullScreenAvatar() async { + private func displayFullScreenAvatar(_ url: URL) async { guard let roomMemberProxy else { fatalError() } - guard let avatarURL = roomMemberProxy.avatarURL else { - return - } - let loadingIndicatorIdentifier = "roomMemberAvatarLoadingIndicator" userIndicatorController.submitIndicator(UserIndicator(id: loadingIndicatorIdentifier, type: .modal, title: L10n.commonLoading, persistent: true)) defer { userIndicatorController.retractIndicatorWithId(loadingIndicatorIdentifier) } // We don't actually know the mime type here, assume it's an image. - if case let .success(file) = await mediaProvider.loadFileFromSource(.init(url: avatarURL, mimeType: "image/jpeg")) { + if case let .success(file) = await mediaProvider.loadFileFromSource(.init(url: url, mimeType: "image/jpeg")) { state.bindings.mediaPreviewItem = MediaPreviewItem(file: file, title: roomMemberProxy.displayName) } } diff --git a/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift b/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift index 1861cd1deb..358b19d968 100644 --- a/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift +++ b/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift @@ -66,8 +66,8 @@ struct RoomMemberDetailsScreen: View { if let memberDetails = context.viewState.memberDetails { AvatarHeaderView(member: memberDetails, avatarSize: .user(on: .memberDetails), - mediaProvider: context.mediaProvider) { - context.send(viewAction: .displayAvatar) + mediaProvider: context.mediaProvider) { url in + context.send(viewAction: .displayAvatar(url)) } footer: { otherUserFooter } diff --git a/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenModels.swift b/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenModels.swift index 83a95a65fa..4cb0647dd8 100644 --- a/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenModels.swift +++ b/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenModels.swift @@ -33,7 +33,7 @@ struct UserProfileScreenViewStateBindings { } enum UserProfileScreenViewAction { - case displayAvatar + case displayAvatar(URL) case openDirectChat case startCall(roomID: String) case dismiss diff --git a/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenViewModel.swift b/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenViewModel.swift index 8abf9be3e2..c409a72890 100644 --- a/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenViewModel.swift +++ b/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenViewModel.swift @@ -74,8 +74,8 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr override func process(viewAction: UserProfileScreenViewAction) { switch viewAction { - case .displayAvatar: - Task { await displayFullScreenAvatar() } + case .displayAvatar(let url): + Task { await displayFullScreenAvatar(url) } case .openDirectChat: Task { await openDirectChat() } case .startCall(let roomID): @@ -87,15 +87,14 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr // MARK: - Private - private func displayFullScreenAvatar() async { + private func displayFullScreenAvatar(_ url: URL) async { guard let userProfile = state.userProfile else { fatalError() } - guard let avatarURL = userProfile.avatarURL else { return } showLoadingIndicator(allowsInteraction: false) defer { hideLoadingIndicator() } // We don't actually know the mime type here, assume it's an image. - if case let .success(file) = await mediaProvider.loadFileFromSource(.init(url: avatarURL, mimeType: "image/jpeg")) { + if case let .success(file) = await mediaProvider.loadFileFromSource(.init(url: url, mimeType: "image/jpeg")) { state.bindings.mediaPreviewItem = MediaPreviewItem(file: file, title: userProfile.displayName) } } diff --git a/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift b/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift index 43201a3bb4..a635e07319 100644 --- a/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift +++ b/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift @@ -63,8 +63,8 @@ struct UserProfileScreen: View { if let userProfile = context.viewState.userProfile { AvatarHeaderView(user: userProfile, avatarSize: .user(on: .memberDetails), - mediaProvider: context.mediaProvider) { - context.send(viewAction: .displayAvatar) + mediaProvider: context.mediaProvider) { url in + context.send(viewAction: .displayAvatar(url)) } footer: { otherUserFooter } From 17dfe1359a3125253e0edcdab7f91ff8a669b05b Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Fri, 27 Sep 2024 13:58:05 +0100 Subject: [PATCH 016/114] Selecting a server that doesn't support login now fails instead of letting you continue to a failure later. (#3342) * Fail configuration of the authentication service if the homeserver doesn't support login. * Move the ServerSelectionCoordinator logic into the ViewModel. - Handle the new login alert. - Add more tests --- ElementX.xcodeproj/project.pbxproj | 4 - .../ServerConfirmationScreenModels.swift | 2 + .../ServerConfirmationScreenViewModel.swift | 8 +- .../MockServerSelectionScreenState.swift | 32 ----- .../ServerSelectionScreenCoordinator.swift | 58 +-------- .../ServerSelectionScreenModels.swift | 6 +- .../ServerSelectionScreenViewModel.swift | 79 ++++++++--- ...rverSelectionScreenViewModelProtocol.swift | 3 - .../View/ServerSelectionScreen.swift | 35 ++++- .../AuthenticationService.swift | 3 + .../AuthenticationServiceProtocol.swift | 1 + .../test_serverSelection-iPad-en-GB.3.png | 4 +- .../test_serverSelection-iPad-pseudo.3.png | 4 +- ...test_serverSelection-iPhone-16-en-GB.3.png | 4 +- ...est_serverSelection-iPhone-16-pseudo.3.png | 4 +- ...rverConfirmationScreenViewModelTests.swift | 30 ++++- .../ServerSelectionViewModelTests.swift | 123 +++++++++++++++--- 17 files changed, 262 insertions(+), 138 deletions(-) delete mode 100644 ElementX/Sources/Screens/Authentication/ServerSelectionScreen/MockServerSelectionScreenState.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 008050994d..fc5cba257d 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -829,7 +829,6 @@ B6DA66EFC13A90846B625836 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 91DE43B8815918E590912DDA /* InfoPlist.strings */; }; B6DF6B6FA8734B70F9BF261E /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5272BC4A60B6AD7553BACA1 /* BlurHashDecode.swift */; }; B6EC2148FA5443C9289BEEBA /* MediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F17EFA1D3D09FC2F9C5E1CB2 /* MediaProvider.swift */; }; - B721125D17A0BA86794F29FB /* MockServerSelectionScreenState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E057FB1F07A5C201C89061 /* MockServerSelectionScreenState.swift */; }; B773ACD8881DB18E876D950C /* WaveformSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94028A227645FA880B966211 /* WaveformSource.swift */; }; B7888FC1E1DEF816D175C8D6 /* SecureBackupKeyBackupScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD72A9B720D75DBE60AC299F /* SecureBackupKeyBackupScreenModels.swift */; }; B796A25F282C0A340D1B9C12 /* ImageRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2B5EDCD05D50BA9B815C66C /* ImageRoomTimelineItemContent.swift */; }; @@ -2141,7 +2140,6 @@ D79BB714D28C9F588DD69353 /* SecureBackupScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupScreenViewModelProtocol.swift; sourceTree = ""; }; D7BB243B26D54EF1A0C422C0 /* NotificationContentBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationContentBuilder.swift; sourceTree = ""; }; D7BEB970F500BFB248443FA1 /* BloomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BloomView.swift; sourceTree = ""; }; - D8E057FB1F07A5C201C89061 /* MockServerSelectionScreenState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockServerSelectionScreenState.swift; sourceTree = ""; }; D8E60332509665C00179ACF6 /* MessageForwardingScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreenViewModel.swift; sourceTree = ""; }; D8F5F9E02B1AB5350B1815E7 /* TimelineStartRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStartRoomTimelineItem.swift; sourceTree = ""; }; D8FC33C3F6BF597E095CE9FA /* HomeScreenInviteCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenInviteCell.swift; sourceTree = ""; }; @@ -2815,7 +2813,6 @@ 2D0D49B0533C4C2EB889BF3A /* ServerSelectionScreen */ = { isa = PBXGroup; children = ( - D8E057FB1F07A5C201C89061 /* MockServerSelectionScreenState.swift */, BB8BC4C791D0E88CFCF4E5DF /* ServerSelectionScreenCoordinator.swift */, 9501D11B4258DFA33BA3B40F /* ServerSelectionScreenModels.swift */, E3059CFA00C67D8787273B20 /* ServerSelectionScreenViewModel.swift */, @@ -6593,7 +6590,6 @@ C97325EFDCCEE457432A9E82 /* MessageText.swift in Sources */, B659E3A49889E749E3239EA7 /* MockMediaProvider.swift in Sources */, 09C83DDDB07C28364F325209 /* MockRoomTimelineController.swift in Sources */, - B721125D17A0BA86794F29FB /* MockServerSelectionScreenState.swift in Sources */, AF2ABA2794E376B64104C964 /* MockSoftLogoutScreenState.swift in Sources */, F9842667B68DC6FA1F9ECCBB /* NSItemProvider.swift in Sources */, EA01A06EEDFEF4AE7652E5F3 /* NSRegularExpresion.swift in Sources */, diff --git a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenModels.swift b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenModels.swift index 811a7fa676..352bb7e28a 100644 --- a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenModels.swift +++ b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenModels.swift @@ -72,6 +72,8 @@ enum ServerConfirmationScreenAlert: Hashable { case invalidWellKnown(String) /// An alert that allows the user to learn about sliding sync. case slidingSync + /// An alert that informs the user that login isn't supported. + case login /// An alert that informs the user that registration isn't supported. case registration /// An unknown error has occurred. diff --git a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenViewModel.swift b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenViewModel.swift index 29563103da..127bf29ed2 100644 --- a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenViewModel.swift +++ b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenViewModel.swift @@ -82,6 +82,8 @@ class ServerConfirmationScreenViewModel: ServerConfirmationScreenViewModelType, displayError(.invalidWellKnown(error)) case .slidingSyncNotAvailable: displayError(.slidingSync) + case .loginNotSupported: + displayError(.login) case .registrationNotSupported: displayError(.registration) default: @@ -117,9 +119,13 @@ class ServerConfirmationScreenViewModel: ServerConfirmationScreenViewModelType, message: L10n.screenChangeServerErrorNoSlidingSyncMessage, primaryButton: .init(title: L10n.actionLearnMore, role: .cancel, action: openURL), secondaryButton: .init(title: L10n.actionCancel, action: nil)) + case .login: + state.bindings.alertInfo = AlertInfo(id: .login, + title: L10n.commonServerNotSupported, + message: L10n.screenLoginErrorUnsupportedAuthentication) case .registration: state.bindings.alertInfo = AlertInfo(id: .registration, - title: L10n.errorUnknown, + title: L10n.commonServerNotSupported, message: L10n.errorAccountCreationNotPossible) case .unknownError: state.bindings.alertInfo = AlertInfo(id: .unknownError) diff --git a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/MockServerSelectionScreenState.swift b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/MockServerSelectionScreenState.swift deleted file mode 100644 index c8393a4bdb..0000000000 --- a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/MockServerSelectionScreenState.swift +++ /dev/null @@ -1,32 +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 SwiftUI - -enum MockServerSelectionScreenState: CaseIterable { - case matrix - case emptyAddress - case invalidAddress - - /// Generate the view struct for the screen state. - @MainActor var viewModel: ServerSelectionScreenViewModel { - switch self { - case .matrix: - return ServerSelectionScreenViewModel(homeserverAddress: "https://matrix.org", - slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL) - - case .emptyAddress: - return ServerSelectionScreenViewModel(homeserverAddress: "", - slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL) - case .invalidAddress: - let viewModel = ServerSelectionScreenViewModel(homeserverAddress: "thisisbad", - slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL) - viewModel.displayError(.footerMessage(L10n.errorUnknown)) - return viewModel - } - } -} diff --git a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenCoordinator.swift b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenCoordinator.swift index 71e78682e1..889b5ef62a 100644 --- a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenCoordinator.swift @@ -37,8 +37,10 @@ final class ServerSelectionScreenCoordinator: CoordinatorProtocol { init(parameters: ServerSelectionScreenCoordinatorParameters) { self.parameters = parameters - viewModel = ServerSelectionScreenViewModel(homeserverAddress: parameters.authenticationService.homeserver.value.address, - slidingSyncLearnMoreURL: parameters.slidingSyncLearnMoreURL) + viewModel = ServerSelectionScreenViewModel(authenticationService: parameters.authenticationService, + authenticationFlow: parameters.authenticationFlow, + slidingSyncLearnMoreURL: parameters.slidingSyncLearnMoreURL, + userIndicatorController: parameters.userIndicatorController) userIndicatorController = parameters.userIndicatorController } @@ -50,8 +52,8 @@ final class ServerSelectionScreenCoordinator: CoordinatorProtocol { guard let self else { return } switch action { - case .confirm(let homeserverAddress): - self.useHomeserver(homeserverAddress) + case .updated: + actionsSubject.send(.updated) case .dismiss: actionsSubject.send(.dismiss) } @@ -60,56 +62,10 @@ final class ServerSelectionScreenCoordinator: CoordinatorProtocol { } func stop() { - stopLoading() + parameters.userIndicatorController.retractAllIndicators() } func toPresentable() -> AnyView { AnyView(ServerSelectionScreen(context: viewModel.context)) } - - // MARK: - Private - - private func startLoading(label: String = L10n.commonLoading) { - userIndicatorController.submitIndicator(UserIndicator(type: .modal, - title: label, - persistent: true)) - } - - private func stopLoading() { - userIndicatorController.retractAllIndicators() - } - - /// Updates the login flow using the supplied homeserver address, or shows an error when this isn't possible. - private func useHomeserver(_ homeserverAddress: String) { - startLoading() - - Task { - switch await authenticationService.configure(for: homeserverAddress, flow: parameters.authenticationFlow) { - case .success: - MXLog.info("Selected homeserver: \(homeserverAddress)") - actionsSubject.send(.updated) - stopLoading() - case .failure(let error): - MXLog.info("Invalid homeserver: \(homeserverAddress)") - stopLoading() - handleError(error) - } - } - } - - /// Processes an error to either update the flow or display it to the user. - private func handleError(_ error: AuthenticationServiceError) { - switch error { - case .invalidServer, .invalidHomeserverAddress: - viewModel.displayError(.footerMessage(L10n.screenChangeServerErrorInvalidHomeserver)) - case .invalidWellKnown(let error): - viewModel.displayError(.invalidWellKnownAlert(error)) - case .slidingSyncNotAvailable: - viewModel.displayError(.slidingSyncAlert) - case .registrationNotSupported: - viewModel.displayError(.registrationAlert) // TODO: [DOUG] Test me! - default: - viewModel.displayError(.footerMessage(L10n.errorUnknown)) - } - } } diff --git a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenModels.swift b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenModels.swift index ae11444bc8..db72a3608b 100644 --- a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenModels.swift +++ b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenModels.swift @@ -8,8 +8,8 @@ import Foundation enum ServerSelectionScreenViewModelAction { - /// The user would like to use the homeserver at the given address. - case confirm(homeserverAddress: String) + /// The homeserver selection has been updated. + case updated /// Dismiss the view without using the entered address. case dismiss } @@ -74,6 +74,8 @@ enum ServerSelectionScreenErrorType: Hashable { case invalidWellKnownAlert(String) /// An alert that allows the user to learn about sliding sync. case slidingSyncAlert + /// An alert that informs the user that login isn't supported. + case loginAlert /// An alert that informs the user that registration isn't supported. case registrationAlert } diff --git a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenViewModel.swift b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenViewModel.swift index fea0915420..a13227b0b7 100644 --- a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenViewModel.swift +++ b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenViewModel.swift @@ -11,7 +11,10 @@ import SwiftUI typealias ServerSelectionScreenViewModelType = StateStoreViewModel class ServerSelectionScreenViewModel: ServerSelectionScreenViewModelType, ServerSelectionScreenViewModelProtocol { + private let authenticationService: AuthenticationServiceProtocol + private let authenticationFlow: AuthenticationFlow private let slidingSyncLearnMoreURL: URL + private let userIndicatorController: UserIndicatorControllerProtocol private var actionsSubject: PassthroughSubject = .init() @@ -19,18 +22,23 @@ class ServerSelectionScreenViewModel: ServerSelectionScreenViewModelType, Server actionsSubject.eraseToAnyPublisher() } - init(homeserverAddress: String, slidingSyncLearnMoreURL: URL) { + init(authenticationService: AuthenticationServiceProtocol, + authenticationFlow: AuthenticationFlow, + slidingSyncLearnMoreURL: URL, + userIndicatorController: UserIndicatorControllerProtocol) { + self.authenticationService = authenticationService + self.authenticationFlow = authenticationFlow self.slidingSyncLearnMoreURL = slidingSyncLearnMoreURL - let bindings = ServerSelectionScreenBindings(homeserverAddress: homeserverAddress) + self.userIndicatorController = userIndicatorController - super.init(initialViewState: ServerSelectionScreenViewState(slidingSyncLearnMoreURL: slidingSyncLearnMoreURL, - bindings: bindings)) + let bindings = ServerSelectionScreenBindings(homeserverAddress: authenticationService.homeserver.value.address) + super.init(initialViewState: ServerSelectionScreenViewState(slidingSyncLearnMoreURL: slidingSyncLearnMoreURL, bindings: bindings)) } override func process(viewAction: ServerSelectionScreenViewAction) { switch viewAction { case .confirm: - actionsSubject.send(.confirm(homeserverAddress: state.bindings.homeserverAddress)) + configureHomeserver() case .dismiss: actionsSubject.send(.dismiss) case .clearFooterError: @@ -38,31 +46,72 @@ class ServerSelectionScreenViewModel: ServerSelectionScreenViewModelType, Server } } - func displayError(_ type: ServerSelectionScreenErrorType) { - switch type { - case .footerMessage(let message): - withElementAnimation { - state.footerErrorMessage = message + // MARK: - Private + + /// Updates the login flow using the supplied homeserver address, or shows an error when this isn't possible. + private func configureHomeserver() { + let homeserverAddress = state.bindings.homeserverAddress + startLoading() + + Task { + switch await authenticationService.configure(for: homeserverAddress, flow: authenticationFlow) { + case .success: + MXLog.info("Selected homeserver: \(homeserverAddress)") + actionsSubject.send(.updated) + stopLoading() + case .failure(let error): + MXLog.info("Invalid homeserver: \(homeserverAddress)") + stopLoading() + handleError(error) } - case .invalidWellKnownAlert(let error): + } + } + + private func startLoading(label: String = L10n.commonLoading) { + userIndicatorController.submitIndicator(UserIndicator(type: .modal, + title: label, + persistent: true)) + } + + private func stopLoading() { + userIndicatorController.retractAllIndicators() + } + + /// Processes an error to either update the flow or display it to the user. + private func handleError(_ error: AuthenticationServiceError) { + switch error { + case .invalidServer, .invalidHomeserverAddress: + showFooterMessage(L10n.screenChangeServerErrorInvalidHomeserver) + case .invalidWellKnown(let error): state.bindings.alertInfo = AlertInfo(id: .invalidWellKnownAlert(error), title: L10n.commonServerNotSupported, message: L10n.screenChangeServerErrorInvalidWellKnown(error)) - case .slidingSyncAlert: + case .slidingSyncNotAvailable: let openURL = { UIApplication.shared.open(self.slidingSyncLearnMoreURL) } state.bindings.alertInfo = AlertInfo(id: .slidingSyncAlert, title: L10n.commonServerNotSupported, message: L10n.screenChangeServerErrorNoSlidingSyncMessage, primaryButton: .init(title: L10n.actionLearnMore, role: .cancel, action: openURL), secondaryButton: .init(title: L10n.actionCancel, action: nil)) - case .registrationAlert: + case .loginNotSupported: + state.bindings.alertInfo = AlertInfo(id: .loginAlert, + title: L10n.commonServerNotSupported, + message: L10n.screenLoginErrorUnsupportedAuthentication) + case .registrationNotSupported: state.bindings.alertInfo = AlertInfo(id: .registrationAlert, - title: L10n.errorUnknown, + title: L10n.commonServerNotSupported, message: L10n.errorAccountCreationNotPossible) + default: + showFooterMessage(L10n.errorUnknown) } } - // MARK: - Private + /// Set a new error message to be shown in the text field footer. + private func showFooterMessage(_ message: String) { + withElementAnimation { + state.footerErrorMessage = message + } + } /// Clear any errors shown in the text field footer. private func clearFooterError() { diff --git a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenViewModelProtocol.swift b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenViewModelProtocol.swift index 320c0842a6..c8f748d60d 100644 --- a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenViewModelProtocol.swift +++ b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/ServerSelectionScreenViewModelProtocol.swift @@ -11,7 +11,4 @@ import Combine protocol ServerSelectionScreenViewModelProtocol { var actions: AnyPublisher { get } var context: ServerSelectionScreenViewModelType.Context { get } - - /// Displays an error to the user. - func displayError(_ type: ServerSelectionScreenErrorType) } diff --git a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/View/ServerSelectionScreen.swift b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/View/ServerSelectionScreen.swift index 03bb915504..122b34a171 100644 --- a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/View/ServerSelectionScreen.swift +++ b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/View/ServerSelectionScreen.swift @@ -91,11 +91,38 @@ struct ServerSelectionScreen: View { // MARK: - Previews struct ServerSelection_Previews: PreviewProvider, TestablePreview { + static let matrixViewModel = makeViewModel(for: "https://matrix.org") + static let emptyViewModel = makeViewModel(for: "") + static let invalidViewModel = makeViewModel(for: "thisisbad") + static var previews: some View { - ForEach(MockServerSelectionScreenState.allCases, id: \.self) { state in - NavigationStack { - ServerSelectionScreen(context: state.viewModel.context) - } + NavigationStack { + ServerSelectionScreen(context: matrixViewModel.context) + } + + NavigationStack { + ServerSelectionScreen(context: emptyViewModel.context) + } + + NavigationStack { + ServerSelectionScreen(context: invalidViewModel.context) + } + .snapshotPreferences(delay: 0.25) + } + + static func makeViewModel(for homeserverAddress: String) -> ServerSelectionScreenViewModel { + let authenticationService = AuthenticationService.mock + + let slidingSyncLearnMoreURL = ServiceLocator.shared.settings.slidingSyncLearnMoreURL + + let viewModel = ServerSelectionScreenViewModel(authenticationService: authenticationService, + authenticationFlow: .login, + slidingSyncLearnMoreURL: slidingSyncLearnMoreURL, + userIndicatorController: UserIndicatorControllerMock()) + viewModel.context.homeserverAddress = homeserverAddress + if homeserverAddress == "thisisbad" { + viewModel.context.send(viewAction: .confirm) } + return viewModel } } diff --git a/ElementX/Sources/Services/Authentication/AuthenticationService.swift b/ElementX/Sources/Services/Authentication/AuthenticationService.swift index 66dbacb2ae..64f5b91f78 100644 --- a/ElementX/Sources/Services/Authentication/AuthenticationService.swift +++ b/ElementX/Sources/Services/Authentication/AuthenticationService.swift @@ -65,6 +65,9 @@ class AuthenticationService: AuthenticationServiceProtocol { case .failure: nil } + if flow == .login, homeserver.loginMode == .unsupported { + return .failure(.loginNotSupported) + } if flow == .register, !homeserver.supportsRegistration { return .failure(.registrationNotSupported) } diff --git a/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift b/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift index 3efa258e90..e762e27cb8 100644 --- a/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift +++ b/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift @@ -24,6 +24,7 @@ enum AuthenticationServiceError: Error, Equatable { case invalidHomeserverAddress case invalidWellKnown(String) case slidingSyncNotAvailable + case loginNotSupported case registrationNotSupported case accountDeactivated case failedLoggingIn diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-en-GB.3.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-en-GB.3.png index 8a65288344..49b86131c9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-en-GB.3.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-en-GB.3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:667d6afdec1cad4db4439278981efc6cafb8c4bb52a28ed951120da64156481c -size 106217 +oid sha256:b24a1036fe61c64214c929d53d409723bf388191205d4392759009680b451ad3 +size 121094 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-pseudo.3.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-pseudo.3.png index ac2cd11120..8014ba2261 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-pseudo.3.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-pseudo.3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:028f43f14f8ad6f5a7eef82c6bcb01779ddec20b6cda44c405ecd874bc3ed35d -size 115393 +oid sha256:8048d1dd4b99792cd4c68567499cfe3aba40a4e3b0697eda3d1b67a115d684f6 +size 145736 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-en-GB.3.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-en-GB.3.png index e1be2f1285..26e485e145 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-en-GB.3.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-en-GB.3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04d2b178fa9a746c0ddef74e83b100011267ba40e98dbcd5ed8d75ec599b89f6 -size 60409 +oid sha256:d6b890b433313f7398c38f940ba1db616b89c563c5ba35e6378b0a4863b9c6b5 +size 75379 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-pseudo.3.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-pseudo.3.png index 4cf24cefd3..45f0a77eed 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-pseudo.3.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-pseudo.3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d05997731f755f45834075ac7dd62cc8b18b94b65354ccd4fb6c198ed91bac1 -size 73627 +oid sha256:1fe38dada4e2201f011443eda61791124e33e5423a049ea4f9b7c942c3405b1b +size 103829 diff --git a/UnitTests/Sources/ServerConfirmationScreenViewModelTests.swift b/UnitTests/Sources/ServerConfirmationScreenViewModelTests.swift index 9f9d3148d1..4a551cdb64 100644 --- a/UnitTests/Sources/ServerConfirmationScreenViewModelTests.swift +++ b/UnitTests/Sources/ServerConfirmationScreenViewModelTests.swift @@ -89,7 +89,7 @@ class ServerConfirmationScreenViewModelTests: XCTestCase { func testRegistrationNotSupportedAlert() async throws { // Given a view model for registration using a service that hasn't been configured and the default server doesn't support registration. - setupViewModel(authenticationFlow: .register, elementWellKnown: false) + setupViewModel(authenticationFlow: .register, supportsRegistrationHelper: false) XCTAssertEqual(service.homeserver.value.loginMode, .unknown) XCTAssertFalse(service.homeserver.value.supportsRegistration) XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0) @@ -105,10 +105,34 @@ class ServerConfirmationScreenViewModelTests: XCTestCase { XCTAssertEqual(context.alertInfo?.id, .registration) } + func testLoginNotSupportedAlert() async throws { + // Given a view model for login using a service that hasn't been configured and the default server doesn't support login. + setupViewModel(authenticationFlow: .login, supportsRegistrationHelper: false, supportsPasswordLogin: false) + XCTAssertEqual(service.homeserver.value.loginMode, .unknown) + XCTAssertFalse(service.homeserver.value.supportsRegistration) + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0) + XCTAssertNil(context.alertInfo) + + // When continuing from the confirmation screen. + let deferred = deferFulfillment(context.$viewState) { $0.bindings.alertInfo != nil } + context.send(viewAction: .confirm) + try await deferred.fulfill() + + // Then the configuration should fail with an alert about not supporting login. + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1) + XCTAssertEqual(context.alertInfo?.id, .login) + } + // MARK: - Helpers - private func setupViewModel(authenticationFlow: AuthenticationFlow, elementWellKnown: Bool = true) { - let client = ClientSDKMock(configuration: elementWellKnown ? .init() : .init(elementWellKnown: "")) + private func setupViewModel(authenticationFlow: AuthenticationFlow, supportsRegistrationHelper: Bool = true, supportsPasswordLogin: Bool = true) { + // Manually create a configuration as the default homeserver address setting is immutable. + let clientConfiguration: ClientSDKMock.Configuration = if supportsRegistrationHelper { + .init(supportsPasswordLogin: supportsPasswordLogin) + } else { + .init(supportsPasswordLogin: supportsPasswordLogin, elementWellKnown: "") + } + let client = ClientSDKMock(configuration: clientConfiguration) let configuration = AuthenticationClientBuilderMock.Configuration(homeserverClients: ["matrix.org": client], qrCodeClient: client) diff --git a/UnitTests/Sources/ServerSelectionViewModelTests.swift b/UnitTests/Sources/ServerSelectionViewModelTests.swift index 7808b62249..fc6411102c 100644 --- a/UnitTests/Sources/ServerSelectionViewModelTests.swift +++ b/UnitTests/Sources/ServerSelectionViewModelTests.swift @@ -11,38 +11,131 @@ import XCTest @MainActor class ServerSelectionViewModelTests: XCTestCase { + var clientBuilderFactory: AuthenticationClientBuilderFactoryMock! + var service: AuthenticationServiceProtocol! + var viewModel: ServerSelectionScreenViewModelProtocol! - var context: ServerSelectionScreenViewModelType.Context! + var context: ServerSelectionScreenViewModelType.Context { viewModel.context } - @MainActor override func setUp() { - viewModel = ServerSelectionScreenViewModel(homeserverAddress: "", - slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL) - context = viewModel.context + func testSelectForLogin() async throws { + // Given a view model for login. + setupViewModel(authenticationFlow: .login) + XCTAssertEqual(service.homeserver.value.loginMode, .unknown) + XCTAssertFalse(service.homeserver.value.supportsRegistration) + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0) + + // When selecting matrix.org. + context.homeserverAddress = "matrix.org" + let deferred = deferFulfillment(viewModel.actions) { $0 == .updated } + context.send(viewAction: .confirm) + try await deferred.fulfill() + + // Then selection should succeed. + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1) + XCTAssertEqual(service.homeserver.value, .mockMatrixDotOrg) } - - func testErrorMessage() async throws { + + func testLoginNotSupportedAlert() async throws { + // Given a view model for login. + setupViewModel(authenticationFlow: .login) + XCTAssertEqual(service.homeserver.value.loginMode, .unknown) + XCTAssertFalse(service.homeserver.value.supportsRegistration) + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0) + XCTAssertNil(context.alertInfo) + + // When selecting a server that doesn't support login. + context.homeserverAddress = "server.net" + let deferred = deferFulfillment(context.$viewState) { $0.bindings.alertInfo != nil } + context.send(viewAction: .confirm) + try await deferred.fulfill() + + // Then selection should fail with an alert about not supporting registration. + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1) + XCTAssertEqual(context.alertInfo?.id, .loginAlert) + } + + func testSelectForRegistration() async throws { + // Given a view model for registration. + setupViewModel(authenticationFlow: .register) + XCTAssertEqual(service.homeserver.value.loginMode, .unknown) + XCTAssertFalse(service.homeserver.value.supportsRegistration) + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0) + + // When selecting matrix.org. + context.homeserverAddress = "matrix.org" + let deferred = deferFulfillment(viewModel.actions) { $0 == .updated } + context.send(viewAction: .confirm) + try await deferred.fulfill() + + // Then selection should succeed. + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1) + XCTAssertEqual(service.homeserver.value, .mockMatrixDotOrg) + } + + func testRegistrationNotSupportedAlert() async throws { + // Given a view model for registration. + setupViewModel(authenticationFlow: .register) + XCTAssertEqual(service.homeserver.value.loginMode, .unknown) + XCTAssertFalse(service.homeserver.value.supportsRegistration) + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 0) + XCTAssertNil(context.alertInfo) + + // When selecting a server that doesn't support registration. + context.homeserverAddress = "example.com" + let deferred = deferFulfillment(context.$viewState) { $0.bindings.alertInfo != nil } + context.send(viewAction: .confirm) + try await deferred.fulfill() + + // Then selection should fail with an alert about not supporting registration. + XCTAssertEqual(clientBuilderFactory.makeBuilderSessionDirectoriesPassphraseClientSessionDelegateAppSettingsAppHooksCallsCount, 1) + XCTAssertEqual(context.alertInfo?.id, .registrationAlert) + } + + func testInvalidServer() async throws { // Given a new instance of the view model. + setupViewModel(authenticationFlow: .login) + XCTAssertFalse(context.viewState.isShowingFooterError, "There should not be an error message for a new view model.") XCTAssertNil(context.viewState.footerErrorMessage, "There should not be an error message for a new view model.") XCTAssertEqual(String(context.viewState.footerMessage.characters), L10n.screenChangeServerFormNotice(L10n.actionLearnMore), "The standard footer message should be shown.") - // When an error occurs. - let message = "Unable to contact server." - viewModel.displayError(.footerMessage(message)) + // When attempting to discover an invalid server + var deferred = deferFulfillment(context.$viewState) { $0.isShowingFooterError } + context.homeserverAddress = "idontexist" + context.send(viewAction: .confirm) + try await deferred.fulfill() // Then the footer should now be showing an error. - XCTAssertEqual(context.viewState.footerErrorMessage, message, "The error message should be stored.") - XCTAssertEqual(String(context.viewState.footerMessage.characters), message, "The error message should be shown.") + XCTAssertTrue(context.viewState.isShowingFooterError, "The error message should be stored.") + XCTAssertNotNil(context.viewState.footerErrorMessage, "The error message should be stored.") + XCTAssertNotEqual(String(context.viewState.footerMessage.characters), L10n.screenChangeServerFormNotice(L10n.actionLearnMore), + "The error message should be shown.") // And when clearing the error. + deferred = deferFulfillment(context.$viewState) { !$0.isShowingFooterError } + context.homeserverAddress = "" context.send(viewAction: .clearFooterError) - - // Wait for the action to spawn a Task. - await Task.yield() + try await deferred.fulfill() // Then the error message should now be removed. XCTAssertNil(context.viewState.footerErrorMessage, "The error message should have been cleared.") XCTAssertEqual(String(context.viewState.footerMessage.characters), L10n.screenChangeServerFormNotice(L10n.actionLearnMore), "The standard footer message should be shown again.") } + + // MARK: - Helpers + + private func setupViewModel(authenticationFlow: AuthenticationFlow) { + clientBuilderFactory = AuthenticationClientBuilderFactoryMock(configuration: .init()) + service = AuthenticationService(userSessionStore: UserSessionStoreMock(configuration: .init()), + encryptionKeyProvider: EncryptionKeyProvider(), + clientBuilderFactory: clientBuilderFactory, + appSettings: ServiceLocator.shared.settings, + appHooks: AppHooks()) + + viewModel = ServerSelectionScreenViewModel(authenticationService: service, + authenticationFlow: authenticationFlow, + slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL, + userIndicatorController: UserIndicatorControllerMock()) + } } From 46263cca42006f921da95fdd5a43aceadebb64da Mon Sep 17 00:00:00 2001 From: Velin92 <34335419+Velin92@users.noreply.github.com> Date: Mon, 30 Sep 2024 00:01:49 +0000 Subject: [PATCH 017/114] Translations update --- .../be.lproj/Localizable.strings | 27 ++++++------- .../bg.lproj/Localizable.strings | 1 + .../cs.lproj/Localizable.strings | 5 ++- .../de.lproj/Localizable.strings | 3 +- .../el.lproj/Localizable.strings | 1 + .../en.lproj/Localizable.strings | 1 + .../es.lproj/Localizable.strings | 1 + .../et.lproj/Localizable.strings | 25 ++++++------ .../fr.lproj/Localizable.strings | 39 ++++++++++--------- .../hu.lproj/Localizable.strings | 1 + .../id.lproj/Localizable.strings | 1 + .../it.lproj/Localizable.strings | 1 + .../ka.lproj/Localizable.strings | 1 + .../nl.lproj/Localizable.strings | 1 + .../pl.lproj/Localizable.strings | 1 + .../pt-BR.lproj/Localizable.strings | 1 + .../pt.lproj/Localizable.strings | 1 + .../ro.lproj/Localizable.strings | 1 + .../ru.lproj/Localizable.strings | 5 ++- .../sk.lproj/Localizable.strings | 11 +++--- .../sv.lproj/Localizable.strings | 1 + .../uk.lproj/Localizable.strings | 1 + .../uz.lproj/Localizable.strings | 1 + .../zh-Hans.lproj/Localizable.strings | 31 ++++++++------- .../zh-Hant-TW.lproj/Localizable.strings | 1 + ElementX/Sources/Generated/Strings.swift | 2 + 26 files changed, 96 insertions(+), 69 deletions(-) diff --git a/ElementX/Resources/Localizations/be.lproj/Localizable.strings b/ElementX/Resources/Localizations/be.lproj/Localizable.strings index 299b8b732a..4660c8f099 100644 --- a/ElementX/Resources/Localizations/be.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/be.lproj/Localizable.strings @@ -33,14 +33,14 @@ "action_close" = "Закрыць"; "action_complete_verification" = "Праверка завершана"; "action_confirm" = "Пацвердзіць"; -"action_confirm_password" = "Confirm password"; +"action_confirm_password" = "Пацвердзіць пароль"; "action_continue" = "Працягнуць"; "action_copy" = "Капіраваць"; "action_copy_link" = "Скапіраваць спасылку"; "action_copy_link_to_message" = "Скапіраваць спасылку на паведамленне"; "action_create" = "Стварыць"; "action_create_a_room" = "Стварыце пакой"; -"action_deactivate" = "Deactivate"; +"action_deactivate" = "Дэактываваць"; "action_decline" = "Адхіліць"; "action_delete_poll" = "Выдаліць апытанне"; "action_disable" = "Адключыць"; @@ -109,12 +109,12 @@ "action_view_source" = "Прагляд зыходнага кода"; "action_yes" = "Так"; "action.load_more" = "Загрузіць больш"; -"action_deactivate_account" = "Deactivate account"; +"action_deactivate_account" = "Дэактываваць уліковы запіс"; "banner_migrate_to_native_sliding_sync_action" = "Выйсці і абнавіць"; -"banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; -"banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; -"banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; +"banner_migrate_to_native_sliding_sync_description" = "Ваш сервер зараз падтрымлівае новы, хутчэйшы пратакол. Выйдзіце з сістэмы і зноў увайдзіце, каб абнавіць яе. Гэта дапаможа вам пазбегнуць прымусовага выхаду з сістэмы, калі стары пратакол будзе пазней выдалены."; +"banner_migrate_to_native_sliding_sync_force_logout_title" = "Ваш хатні сервер больш не падтрымлівае стары пратакол. Калі ласка, выйдзіце і ўвайдзіце зноў, каб працягнуць выкарыстанне праграмы."; +"banner_migrate_to_native_sliding_sync_title" = "Даступна абнаўленне"; +"banner.set_up_recovery.content" = "Стварыце новы ключ аднаўлення, які можна выкарыстоўваць для аднаўлення зашыфраванай гісторыі паведамленняў у выпадку страты доступу да вашых прылад."; "banner.set_up_recovery.title" = "Наладзіць аднаўленне"; "common_about" = "Аб праграме"; "common_acceptable_use_policy" = "Палітыка дапушчальнага выкарыстання"; @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Ліцэнзіі з адкрытым зыходным кодам"; "common.pinned" = "Замацаваны"; "common.send_to" = "Адправіць"; +"common.you" = "Вы"; "common_no_room_name" = "Няма назвы пакоя"; "common_poll_end_confirmation" = "Вы ўпэўнены, што хочаце скончыць гэтае апытанне?"; "common_poll_summary" = "Апытанне: %1$@"; @@ -258,7 +259,7 @@ "emoji_picker_category_people" = "Усмешкі & Удзельнікі"; "emoji_picker_category_places" = "Падарожжы & Месцы"; "emoji_picker_category_symbols" = "Сімвалы"; -"error_account_creation_not_possible" = "Your homeserver needs to be upgraded to support Matrix Authentication Service and account creation."; +"error_account_creation_not_possible" = "Ваш хатні сервер неабходна абнавіць для падтрымкі Matrix Authentication Service і стварэння ўліковага запісу."; "error_failed_creating_the_permalink" = "Не атрымалася стварыць пастаянную спасылку"; "error_failed_loading_map" = "%1$@ не атрымалася загрузіць карту. Калі ласка паспрабуйце зноў пазней."; "error_failed_loading_messages" = "Не ўдалося загрузіць паведамленні"; @@ -269,7 +270,7 @@ "error_some_messages_have_not_been_sent" = "Некаторыя паведамленні не былі адпраўлены"; "error_unknown" = "Выбачце, адбылася памылка"; "event_shield_reason_authenticity_not_guaranteed" = "Сапраўднасць гэтага зашыфраванага паведамлення не можа быць гарантаваная на гэтай прыладзе."; -"event_shield_reason_previously_verified" = "Encrypted by a previously-verified user."; +"event_shield_reason_previously_verified" = "Зашыфравана раней правераным карыстальнікам."; "event_shield_reason_sent_in_clear" = "Не зашыфраваны."; "event_shield_reason_unknown_device" = "Зашыфравана невядомай ці выдаленай прыладай."; "event_shield_reason_unsigned_device" = "Зашыфравана прыладай, не пацверджанай яе ўладальнікам."; @@ -292,12 +293,12 @@ "notification_inline_reply_failed" = "** Не атрымалася даслаць - калі ласка, адкрыйце пакой"; "notification_invitation_action_reject" = "Адхіліць"; "notification_invite_body" = "Запрасіў(-ла) вас у чат"; -"notification_invite_body_with_sender" = "%1$@ invited you to chat"; +"notification_invite_body_with_sender" = "%1$@ запрасіў(-ла) вас у чат"; "notification_mentioned_you_body" = "Згадаў(-ла) вас: %1$@"; "notification_new_messages" = "Новыя паведамленні"; "notification_reaction_body" = "Адрэагаваў(-ла) на %1$@"; "notification_room_invite_body" = "Запрасіў(-ла) вас далучыцца да пакоя"; -"notification_room_invite_body_with_sender" = "%1$@ invited you to join the room"; +"notification_room_invite_body_with_sender" = "%1$@ запрасіў(-ла) вас далучыцца да пакоя"; "notification_sender_me" = "Я"; "notification_sender_mention_reply" = "%1$@ mentioned or replied"; "notification_test_push_notification_content" = "Вы праглядаеце апавяшчэнне! Націсніце мяне!"; @@ -336,7 +337,7 @@ "screen_pinned_timeline_empty_state_headline" = "Замацуеце важныя паведамленні, каб іх можна было лёгка знайсці"; "screen_pinned_timeline_screen_title_empty" = "Замацаваныя паведамленні"; "screen_reset_encryption_password_error" = "Адбылася невядомая памылка. Калі ласка, праверце правільнасць пароля вашага ўліковага запісу і паўтарыце спробу."; -"screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; +"screen_resolve_send_failure_changed_identity_primary_button_title" = "Адклікаць праверку і адправіць"; "screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; "screen_resolve_send_failure_changed_identity_title" = "Your message was not sent because %1$@’s verified identity has changed"; "screen_resolve_send_failure_unsigned_device_primary_button_title" = "Усё роўна адправіць паведамленне"; @@ -469,7 +470,7 @@ "screen_deactivate_account_list_item_2" = "Remove you from all chat rooms."; "screen_deactivate_account_list_item_3" = "Delete your account information from our identity server."; "screen_deactivate_account_list_item_4" = "Your messages will still be visible to registered users but won’t be available to new or unregistered users if you choose to delete them."; -"screen_deactivate_account_title" = "Deactivate account"; +"screen_deactivate_account_title" = "Дэактываваць уліковы запіс"; "screen_edit_poll_delete_confirmation" = "Вы ўпэўнены, што хочаце выдаліць гэтае апытанне?"; "screen_edit_profile_display_name" = "Бачнае імя"; "screen_edit_profile_display_name_placeholder" = "Ваша бачнае імя"; diff --git a/ElementX/Resources/Localizations/bg.lproj/Localizable.strings b/ElementX/Resources/Localizations/bg.lproj/Localizable.strings index 357c221e5b..1bc78b28e7 100644 --- a/ElementX/Resources/Localizations/bg.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/bg.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "Send to"; +"common.you" = "You"; "common_no_room_name" = "No room name"; "common_poll_end_confirmation" = "Сигурни ли сте, че искате да приключите тази анкета?"; "common_poll_summary" = "Анкета: %1$@"; diff --git a/ElementX/Resources/Localizations/cs.lproj/Localizable.strings b/ElementX/Resources/Localizations/cs.lproj/Localizable.strings index 0966a7b0cf..eb7bc8edb2 100644 --- a/ElementX/Resources/Localizations/cs.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/cs.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Licence s otevřeným zdrojovým kódem"; "common.pinned" = "Připnuto"; "common.send_to" = "Odeslat do"; +"common.you" = "Vy"; "common_no_room_name" = "Žádný název místnosti"; "common_poll_end_confirmation" = "Opravdu chcete ukončit toto hlasování?"; "common_poll_summary" = "Hlasování: %1$@"; @@ -292,12 +293,12 @@ "notification_inline_reply_failed" = "** Nepodařilo se odeslat - otevřete prosím místnost"; "notification_invitation_action_reject" = "Odmítnout"; "notification_invite_body" = "Vás pozval(a) do chatu"; -"notification_invite_body_with_sender" = "%1$@ invited you to chat"; +"notification_invite_body_with_sender" = "%1$@ vás pozval(a) do chatu"; "notification_mentioned_you_body" = "Zmínili vás: %1$@"; "notification_new_messages" = "Nové zprávy"; "notification_reaction_body" = "Reagoval(a) s %1$@"; "notification_room_invite_body" = "Vás pozval(a) do místnosti"; -"notification_room_invite_body_with_sender" = "%1$@ invited you to join the room"; +"notification_room_invite_body_with_sender" = "%1$@ vás pozval(a) do místnosti"; "notification_sender_me" = "Já"; "notification_sender_mention_reply" = "%1$@ zmínil(a) nebo odpověděl(a)"; "notification_test_push_notification_content" = "Prohlížíte si oznámení! Klikněte na mě!"; diff --git a/ElementX/Resources/Localizations/de.lproj/Localizable.strings b/ElementX/Resources/Localizations/de.lproj/Localizable.strings index aea080e28b..0716de2081 100644 --- a/ElementX/Resources/Localizations/de.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/de.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Open-Source-Lizenzen"; "common.pinned" = "Fixiert"; "common.send_to" = "Senden an"; +"common.you" = "You"; "common_no_room_name" = "Kein Raumname"; "common_poll_end_confirmation" = "Bist du sicher, dass du diese Umfrage beenden möchtest?"; "common_poll_summary" = "Umfrage: %1$@"; @@ -346,7 +347,7 @@ "screen_resolve_send_failure_you_unsigned_device_title" = "Your message was not sent because you have not verified one or more of your devices"; "screen_room_mentions_at_room_subtitle" = "Alle Mitglieder benachrichtigen"; "screen_room_pinned_banner_indicator" = "%1$@ von %2$@"; -"screen_room_pinned_banner_indicator_description" = "%1$@ Angepinnte Nachrichten"; +"screen_room_pinned_banner_indicator_description" = "%1$@ fixierte Nachrichten"; "screen_room_pinned_banner_loading_description" = "Nachricht wird geladen…"; "screen_room_pinned_banner_view_all_button_title" = "Alle anzeigen"; "screen_room_details_pinned_events_row_title" = "Fixierte Nachrichten"; diff --git a/ElementX/Resources/Localizations/el.lproj/Localizable.strings b/ElementX/Resources/Localizations/el.lproj/Localizable.strings index b4570d38a9..3e5d0e3151 100644 --- a/ElementX/Resources/Localizations/el.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/el.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Άδειες ανοιχτού κώδικα"; "common.pinned" = "Καρφιτσωμένο"; "common.send_to" = "Αποστολή σε"; +"common.you" = "You"; "common_no_room_name" = "Χωρίς όνομα δωματίου"; "common_poll_end_confirmation" = "Θες σίγουρα να τερματίσεις αυτή τη δημοσκόπηση;"; "common_poll_summary" = "Δημοσκόπηση: %1$@"; diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index ad6dae5baa..f592805304 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "Send to"; +"common.you" = "You"; "common_no_room_name" = "No room name"; "common_poll_end_confirmation" = "Are you sure you want to end this poll?"; "common_poll_summary" = "Poll: %1$@"; diff --git a/ElementX/Resources/Localizations/es.lproj/Localizable.strings b/ElementX/Resources/Localizations/es.lproj/Localizable.strings index 7a6f58f87c..9fc09d4489 100644 --- a/ElementX/Resources/Localizations/es.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/es.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "Send to"; +"common.you" = "You"; "common_no_room_name" = "No room name"; "common_poll_end_confirmation" = "¿Estás seguro de que quieres finalizar esta encuesta?"; "common_poll_summary" = "Encuesta: %1$@"; diff --git a/ElementX/Resources/Localizations/et.lproj/Localizable.strings b/ElementX/Resources/Localizations/et.lproj/Localizable.strings index fdc0703ecb..4d814ab2b0 100644 --- a/ElementX/Resources/Localizations/et.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/et.lproj/Localizable.strings @@ -33,14 +33,14 @@ "action_close" = "Sulge"; "action_complete_verification" = "Tee verifitseerimine lõpuni"; "action_confirm" = "Kinnita"; -"action_confirm_password" = "Korda salasõna"; +"action_confirm_password" = "Kinnita otsust oma salasõnaga"; "action_continue" = "Jätka"; "action_copy" = "Kopeeri"; "action_copy_link" = "Kopeeri link"; "action_copy_link_to_message" = "Kopeeri sõnumi link"; "action_create" = "Loo"; "action_create_a_room" = "Loo jututuba"; -"action_deactivate" = "Deactivate"; +"action_deactivate" = "Eemalda konto"; "action_decline" = "Keeldu"; "action_delete_poll" = "Kustuta küsitlus"; "action_disable" = "Lülita välja"; @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Avatud lähtekoodiga litsentsid"; "common.pinned" = "Esiletõstetud"; "common.send_to" = "Saada kasutajale"; +"common.you" = "Sina"; "common_no_room_name" = "Jututoal puudub nimi"; "common_poll_end_confirmation" = "Kas oled kindel, et soovid selle küsitluse lõpetada?"; "common_poll_summary" = "Küsitlus: %1$@"; @@ -292,12 +293,12 @@ "notification_inline_reply_failed" = "** Saatmine ei õnnestunud - palun ava jututoa täisvaade"; "notification_invitation_action_reject" = "Lükka tagasi"; "notification_invite_body" = "Kutse osalema vestluses"; -"notification_invite_body_with_sender" = "%1$@ invited you to chat"; +"notification_invite_body_with_sender" = "%1$@ saatus sulle vestluskutse"; "notification_mentioned_you_body" = "Mainis sind: %1$@"; "notification_new_messages" = "Uued sõnumid"; "notification_reaction_body" = "Reageeris nii: %1$@"; "notification_room_invite_body" = "Saatis sulle kutse jututuppa"; -"notification_room_invite_body_with_sender" = "%1$@ invited you to join the room"; +"notification_room_invite_body_with_sender" = "%1$@ saatis sulle kutse jututoaga liitumiseks"; "notification_sender_me" = "Mina"; "notification_sender_mention_reply" = "%1$@ mainis või vastas"; "notification_test_push_notification_content" = "See ongi teavitus! Klõpsi mind!"; @@ -461,14 +462,14 @@ "screen_create_room_topic_label" = "Teema (kui soovid lisada)"; "screen_deactivate_account_confirmation_dialog_content" = "Palun kinnita uuesti, et soovid eemaldada oma konto kasutusest"; "screen_deactivate_account_delete_all_messages" = "Kustuta kõik minu sõnumid"; -"screen_deactivate_account_delete_all_messages_notice" = "Warning: Future users may see incomplete conversations."; -"screen_deactivate_account_description" = "Deactivating your account is %1$@, it will:"; -"screen_deactivate_account_description_bold_part" = "irreversible"; -"screen_deactivate_account_list_item_1" = "%1$@ your account (you can't log back in, and your ID can't be reused)."; -"screen_deactivate_account_list_item_1_bold_part" = "Permanently disable"; -"screen_deactivate_account_list_item_2" = "Remove you from all chat rooms."; -"screen_deactivate_account_list_item_3" = "Delete your account information from our identity server."; -"screen_deactivate_account_list_item_4" = "Your messages will still be visible to registered users but won’t be available to new or unregistered users if you choose to delete them."; +"screen_deactivate_account_delete_all_messages_notice" = "Hoiatus: tulevased kasutajad võivad näha poolikuid vestlusi."; +"screen_deactivate_account_description" = "Sinu konto kasutusest eemaldamine on %1$@ ja sellega:"; +"screen_deactivate_account_description_bold_part" = "pöördumatu"; +"screen_deactivate_account_list_item_1" = "Sinu kasutajakonto %1$@ (sa ei saa enam sellega võrku logida ning kasutajatunnust ei saa enam pruukida)."; +"screen_deactivate_account_list_item_1_bold_part" = "jäädavalt eemaldatakse kasutusest"; +"screen_deactivate_account_list_item_2" = "Sind logitakse välja kõikidest jututubadest."; +"screen_deactivate_account_list_item_3" = "Kustutatakse sinu andmed meie isikutuvastusserverist."; +"screen_deactivate_account_list_item_4" = "Sinu sõnumid on jätkuvalt nähtavad registreeritud kasutajatele, kuid kui otsustad sõnumid kustutada, siis nad nad pole nähtavad uutele ja registreerimata kasutajatele."; "screen_deactivate_account_title" = "Eemalda konto kasutusest"; "screen_edit_poll_delete_confirmation" = "Kas sa oled kindel, et soovid selle küsitluse kustutada?"; "screen_edit_profile_display_name" = "Kuvatav nimi"; diff --git a/ElementX/Resources/Localizations/fr.lproj/Localizable.strings b/ElementX/Resources/Localizations/fr.lproj/Localizable.strings index ae8d717f79..9cb2420158 100644 --- a/ElementX/Resources/Localizations/fr.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/fr.lproj/Localizable.strings @@ -33,14 +33,14 @@ "action_close" = "Fermer"; "action_complete_verification" = "Terminer la vérification"; "action_confirm" = "Confirmer"; -"action_confirm_password" = "Confirm password"; +"action_confirm_password" = "Confirmez le mot de passe"; "action_continue" = "Continuer"; "action_copy" = "Copier"; "action_copy_link" = "Copier le lien"; "action_copy_link_to_message" = "Copier le lien vers le message"; "action_create" = "Créer"; "action_create_a_room" = "Créer un salon"; -"action_deactivate" = "Deactivate"; +"action_deactivate" = "Désactiver"; "action_decline" = "Refuser"; "action_delete_poll" = "Supprimer le sondage"; "action_disable" = "Désactiver"; @@ -109,7 +109,7 @@ "action_view_source" = "Afficher la source"; "action_yes" = "Oui"; "action.load_more" = "Voir plus"; -"action_deactivate_account" = "Deactivate account"; +"action_deactivate_account" = "Désactiver le compte"; "banner_migrate_to_native_sliding_sync_action" = "Déconnecter et mettre à niveau"; "banner_migrate_to_native_sliding_sync_description" = "Votre serveur prend désormais en charge un nouveau protocole plus rapide. Déconnectez-vous, puis reconnectez-vous pour effectuer la mise à niveau dès maintenant. En le faisant tout de suite, vous éviterez une déconnexion forcée lorsque l'ancien protocole sera supprimé."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Votre serveur d’accueil ne prend plus en charge l'ancien protocole. Veuillez vous déconnecter puis vous reconnecter pour continuer à utiliser l'application."; @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Licences open source"; "common.pinned" = "Épinglé"; "common.send_to" = "Envoyer vers"; +"common.you" = "Vous"; "common_no_room_name" = "Salon sans nom"; "common_poll_end_confirmation" = "Êtes-vous sûr de vouloir mettre fin à ce sondage ?"; "common_poll_summary" = "Sondage : %1$@"; @@ -292,12 +293,12 @@ "notification_inline_reply_failed" = "** Échec de l’envoi - veuillez ouvrir le salon"; "notification_invitation_action_reject" = "Rejeter"; "notification_invite_body" = "Vous a invité(e) à discuter"; -"notification_invite_body_with_sender" = "%1$@ invited you to chat"; +"notification_invite_body_with_sender" = "%1$@ vous a invité à discuter"; "notification_mentioned_you_body" = "Mentionné(e): %1$@"; "notification_new_messages" = "Nouveaux messages"; "notification_reaction_body" = "A réagi avec %1$@"; "notification_room_invite_body" = "Vous a invité(e) à rejoindre le salon"; -"notification_room_invite_body_with_sender" = "%1$@ invited you to join the room"; +"notification_room_invite_body_with_sender" = "%1$@ vous a invité à rejoindre le salon"; "notification_sender_me" = "Moi"; "notification_sender_mention_reply" = "%1$@ mentionné ou en réponse"; "notification_test_push_notification_content" = "Vous êtes en train de voir la notification ! Cliquez-moi !"; @@ -342,8 +343,8 @@ "screen_resolve_send_failure_unsigned_device_primary_button_title" = "Envoyer le message quand même"; "screen_resolve_send_failure_unsigned_device_subtitle" = "%1$@ utilise un ou plusieurs appareils non vérifiés. Vous pouvez quand même envoyer le message, ou vous pouvez annuler pour l'instant et réessayer plus tard après que %2$@ vérifie tous ses appareils."; "screen_resolve_send_failure_unsigned_device_title" = "Votre message n'a pas été envoyé car %1$@ n'a pas vérifié tous ses appareils"; -"screen_resolve_send_failure_you_unsigned_device_subtitle" = "One or more of your devices are unverified. You can send the message anyway, or you can cancel for now and try again later after you have verified all of your devices."; -"screen_resolve_send_failure_you_unsigned_device_title" = "Your message was not sent because you have not verified one or more of your devices"; +"screen_resolve_send_failure_you_unsigned_device_subtitle" = "Un ou plusieurs de vos appareils ne sont pas vérifiés. Vous pouvez quand même envoyer le message, ou vous pouvez annuler et réessayer plus tard après avoir vérifié tous vos appareils."; +"screen_resolve_send_failure_you_unsigned_device_title" = "Votre message n'a pas été envoyé car vous n'avez pas vérifié tous vos appareils"; "screen_room_mentions_at_room_subtitle" = "Notifier tout le salon"; "screen_room_pinned_banner_indicator" = "%1$@ sur %2$@"; "screen_room_pinned_banner_indicator_description" = "%1$@ Messages épinglés"; @@ -352,7 +353,7 @@ "screen_room_details_pinned_events_row_title" = "Messages épinglés"; "screen_timeline_item_menu_send_failure_changed_identity" = "Le message n'a pas été envoyé car l'identité vérifiée de %1$@ a changé."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Le message n'a pas été envoyé car %1$@ n'a pas vérifié tous ses appareils."; -"screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; +"screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message non envoyé car vous n'avez pas vérifié tous vos appareils."; "screen_account_provider_change" = "Changer de fournisseur de compte"; "screen_account_provider_form_hint" = "Adresse du serveur d’accueil"; "screen_account_provider_form_notice" = "Entrez un terme de recherche ou une adresse de domaine."; @@ -459,17 +460,17 @@ "screen_create_room_public_option_description" = "Les messages ne sont pas chiffrés et n’importe qui peut les lire. Vous pouvez activer le chiffrement ultérieurement."; "screen_create_room_public_option_title" = "Salon public (tout le monde)"; "screen_create_room_topic_label" = "Sujet (facultatif)"; -"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"; -"screen_deactivate_account_delete_all_messages_notice" = "Warning: Future users may see incomplete conversations."; -"screen_deactivate_account_description" = "Deactivating your account is %1$@, it will:"; -"screen_deactivate_account_description_bold_part" = "irreversible"; -"screen_deactivate_account_list_item_1" = "%1$@ your account (you can't log back in, and your ID can't be reused)."; -"screen_deactivate_account_list_item_1_bold_part" = "Permanently disable"; -"screen_deactivate_account_list_item_2" = "Remove you from all chat rooms."; -"screen_deactivate_account_list_item_3" = "Delete your account information from our identity server."; -"screen_deactivate_account_list_item_4" = "Your messages will still be visible to registered users but won’t be available to new or unregistered users if you choose to delete them."; -"screen_deactivate_account_title" = "Deactivate account"; +"screen_deactivate_account_confirmation_dialog_content" = "Veuillez confirmer que vous souhaitez désactiver votre compte. Cette action ne peut pas être annulée."; +"screen_deactivate_account_delete_all_messages" = "Supprimer tous mes messages"; +"screen_deactivate_account_delete_all_messages_notice" = "Attention : les futurs utilisateurs pourraient voir des conversations incomplètes."; +"screen_deactivate_account_description" = "La désactivation de votre compte est %1$@, cela va:"; +"screen_deactivate_account_description_bold_part" = "irréversible"; +"screen_deactivate_account_list_item_1" = "%1$@ votre compte (vous ne pourrez plus vous reconnecter et votre identifiant ne pourra pas être réutilisé)."; +"screen_deactivate_account_list_item_1_bold_part" = "Désactiver définitivement"; +"screen_deactivate_account_list_item_2" = "Vous retirer de tous les salons et toutes les discussions."; +"screen_deactivate_account_list_item_3" = "Supprimer les informations de votre compte du serveur d'identité."; +"screen_deactivate_account_list_item_4" = "Rendre vos messages invisibles aux futurs membres des salons si vous choisissez de les supprimer. Vos messages seront toujours visibles pour les utilisateurs qui les ont déjà récupérés."; +"screen_deactivate_account_title" = "Désactiver votre compte"; "screen_edit_poll_delete_confirmation" = "Êtes-vous certain de vouloir supprimer ce sondage?"; "screen_edit_profile_display_name" = "Pseudonyme"; "screen_edit_profile_display_name_placeholder" = "Votre pseudonyme"; diff --git a/ElementX/Resources/Localizations/hu.lproj/Localizable.strings b/ElementX/Resources/Localizations/hu.lproj/Localizable.strings index e1cef07ed6..67524fccb6 100644 --- a/ElementX/Resources/Localizations/hu.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/hu.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Nyílt forráskódú licencek"; "common.pinned" = "Kitűzve"; "common.send_to" = "Címzett"; +"common.you" = "You"; "common_no_room_name" = "Nincs szobanév"; "common_poll_end_confirmation" = "Biztos, hogy befejezi ezt a szavazást?"; "common_poll_summary" = "Szavazás: %1$@"; diff --git a/ElementX/Resources/Localizations/id.lproj/Localizable.strings b/ElementX/Resources/Localizations/id.lproj/Localizable.strings index 2712368036..10770d9c34 100644 --- a/ElementX/Resources/Localizations/id.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/id.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "Kirim ke"; +"common.you" = "You"; "common_no_room_name" = "Tidak ada nama ruangan"; "common_poll_end_confirmation" = "Apakah Anda yakin ingin mengakhiri pemungutan suara ini?"; "common_poll_summary" = "Pemungutan suara: %1$@"; diff --git a/ElementX/Resources/Localizations/it.lproj/Localizable.strings b/ElementX/Resources/Localizations/it.lproj/Localizable.strings index f3b48eb9dc..2a7b8e2258 100644 --- a/ElementX/Resources/Localizations/it.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/it.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Licenze open source"; "common.pinned" = "Pinned"; "common.send_to" = "Invia a"; +"common.you" = "You"; "common_no_room_name" = "Nessun nome della stanza"; "common_poll_end_confirmation" = "Vuoi davvero terminare questo sondaggio?"; "common_poll_summary" = "Sondaggio: %1$@"; diff --git a/ElementX/Resources/Localizations/ka.lproj/Localizable.strings b/ElementX/Resources/Localizations/ka.lproj/Localizable.strings index c8c3acb277..421186f9bb 100644 --- a/ElementX/Resources/Localizations/ka.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ka.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "Send to"; +"common.you" = "You"; "common_no_room_name" = "No room name"; "common_poll_end_confirmation" = "დარწმუნებული ხართ, რომ გსურთ ამ გამოკითხვის დასრულება?"; "common_poll_summary" = "გამოკითხვა: %1$@"; diff --git a/ElementX/Resources/Localizations/nl.lproj/Localizable.strings b/ElementX/Resources/Localizations/nl.lproj/Localizable.strings index b340743bc5..eb86c71edd 100644 --- a/ElementX/Resources/Localizations/nl.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/nl.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "Send to"; +"common.you" = "You"; "common_no_room_name" = "Geen kamernaam"; "common_poll_end_confirmation" = "Weet je zeker dat je deze peiling wilt beëindigen?"; "common_poll_summary" = "Peiling: %1$@"; diff --git a/ElementX/Resources/Localizations/pl.lproj/Localizable.strings b/ElementX/Resources/Localizations/pl.lproj/Localizable.strings index 1c594d2565..17f52505f6 100644 --- a/ElementX/Resources/Localizations/pl.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pl.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Licencje open-source"; "common.pinned" = "Pinned"; "common.send_to" = "Wyślij do"; +"common.you" = "You"; "common_no_room_name" = "Brak nazwy pokoju"; "common_poll_end_confirmation" = "Jesteś pewien, że chcesz zakończyć tę ankietę?"; "common_poll_summary" = "Ankieta: %1$@"; diff --git a/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings b/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings index 0b4aa301e5..163228042b 100644 --- a/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Licenças de código aberto"; "common.pinned" = "Pinned"; "common.send_to" = "Enviar para"; +"common.you" = "You"; "common_no_room_name" = "Sem nome de sala"; "common_poll_end_confirmation" = "Tem certeza de que deseja encerrar esta enquete?"; "common_poll_summary" = "Enquete: %1$@"; diff --git a/ElementX/Resources/Localizations/pt.lproj/Localizable.strings b/ElementX/Resources/Localizations/pt.lproj/Localizable.strings index b385910f11..b7bc7ca582 100644 --- a/ElementX/Resources/Localizations/pt.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pt.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Licenças de código aberto"; "common.pinned" = "Pinned"; "common.send_to" = "Enviar para"; +"common.you" = "You"; "common_no_room_name" = "Sala sem nome"; "common_poll_end_confirmation" = "Tens a certeza que queres concluir esta sondagem?"; "common_poll_summary" = "Sondagem: %1$@"; diff --git a/ElementX/Resources/Localizations/ro.lproj/Localizable.strings b/ElementX/Resources/Localizations/ro.lproj/Localizable.strings index 595d4b3690..6ab8b1f96c 100644 --- a/ElementX/Resources/Localizations/ro.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ro.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "Trimiteți către"; +"common.you" = "You"; "common_no_room_name" = "Fără nume de cameră"; "common_poll_end_confirmation" = "Sunteți sigur că doriți să încheiați acest sondaj?"; "common_poll_summary" = "Sondajul %1$@"; diff --git a/ElementX/Resources/Localizations/ru.lproj/Localizable.strings b/ElementX/Resources/Localizations/ru.lproj/Localizable.strings index 4c044fa74f..eed53c51b3 100644 --- a/ElementX/Resources/Localizations/ru.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ru.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Лицензии с открытым исходным кодом"; "common.pinned" = "Закрепленный"; "common.send_to" = "Отправить"; +"common.you" = "You"; "common_no_room_name" = "Нету названия комнаты"; "common_poll_end_confirmation" = "Вы действительно хотите завершить данный опрос?"; "common_poll_summary" = "Опрос: %1$@"; @@ -292,12 +293,12 @@ "notification_inline_reply_failed" = "** Не удалось отправить - пожалуйста, откройте комнату"; "notification_invitation_action_reject" = "Отклонить"; "notification_invite_body" = "Пригласил вас в чат"; -"notification_invite_body_with_sender" = "%1$@ invited you to chat"; +"notification_invite_body_with_sender" = "%1$@ пригласил вас в чат"; "notification_mentioned_you_body" = "Упомянул вас: %1$@"; "notification_new_messages" = "Новые сообщения"; "notification_reaction_body" = "Отреагировал на %1$@"; "notification_room_invite_body" = "Пригласил вас в комнату"; -"notification_room_invite_body_with_sender" = "%1$@ invited you to join the room"; +"notification_room_invite_body_with_sender" = "%1$@ пригласил вас присоединиться к комнате"; "notification_sender_me" = "Я"; "notification_sender_mention_reply" = "%1$@ упомянул или ответил"; "notification_test_push_notification_content" = "Вы просматриваете уведомление! Нажмите на меня!"; diff --git a/ElementX/Resources/Localizations/sk.lproj/Localizable.strings b/ElementX/Resources/Localizations/sk.lproj/Localizable.strings index dba24dba2a..326537163a 100644 --- a/ElementX/Resources/Localizations/sk.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/sk.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Licencie s otvoreným zdrojom"; "common.pinned" = "Pripnuté"; "common.send_to" = "Odoslať"; +"common.you" = "Vy"; "common_no_room_name" = "Žiadny názov miestnosti"; "common_poll_end_confirmation" = "Ste si istí, že chcete ukončiť túto anketu?"; "common_poll_summary" = "Anketa: %1$@"; @@ -292,12 +293,12 @@ "notification_inline_reply_failed" = "** Nepodarilo sa odoslať - prosím otvorte miestnosť"; "notification_invitation_action_reject" = "Zamietnuť"; "notification_invite_body" = "Vás pozval/a na konverzáciu"; -"notification_invite_body_with_sender" = "%1$@ invited you to chat"; +"notification_invite_body_with_sender" = "%1$@ vás pozval/a na rozhovor"; "notification_mentioned_you_body" = "Spomenul/a vás: %1$@"; "notification_new_messages" = "Nové správy"; "notification_reaction_body" = "Reagoval/a s %1$@"; "notification_room_invite_body" = "Vás pozval do miestnosti"; -"notification_room_invite_body_with_sender" = "%1$@ invited you to join the room"; +"notification_room_invite_body_with_sender" = "%1$@ vás pozval/a, aby ste sa pripojili k miestnosti"; "notification_sender_me" = "Ja"; "notification_sender_mention_reply" = "%1$@ spomenul/a alebo odpovedal/a"; "notification_test_push_notification_content" = "Prezeráte si oznámenie! Kliknite na mňa!"; @@ -464,10 +465,10 @@ "screen_deactivate_account_delete_all_messages_notice" = "Upozornenie: Budúcim používateľom sa môžu zobraziť neúplné konverzácie."; "screen_deactivate_account_description" = "Deaktivácia vášho účtu znamená %1$@, že:"; "screen_deactivate_account_description_bold_part" = "nezvratný"; -"screen_deactivate_account_list_item_1" = "%1$@ your account (you can't log back in, and your ID can't be reused)."; +"screen_deactivate_account_list_item_1" = "%1$@ váš účet (nebudete sa môcť znova prihlásiť a vaše ID nebude možné znova použiť)."; "screen_deactivate_account_list_item_1_bold_part" = "Natrvalo zakázať"; -"screen_deactivate_account_list_item_2" = "Remove you from all chat rooms."; -"screen_deactivate_account_list_item_3" = "Delete your account information from our identity server."; +"screen_deactivate_account_list_item_2" = "Odstrániť vás zo všetkých miestností."; +"screen_deactivate_account_list_item_3" = "Odstrániť informácie o vašom účte z nášho servera totožností."; "screen_deactivate_account_list_item_4" = "Vaše správy budú stále viditeľné pre registrovaných používateľov, ale nebudú dostupné pre nových alebo neregistrovaných používateľov, ak sa ich rozhodnete odstrániť."; "screen_deactivate_account_title" = "Deaktivovať účet"; "screen_edit_poll_delete_confirmation" = "Ste si istý, že chcete odstrániť túto anketu?"; diff --git a/ElementX/Resources/Localizations/sv.lproj/Localizable.strings b/ElementX/Resources/Localizations/sv.lproj/Localizable.strings index db7a12b982..71daaf32ea 100644 --- a/ElementX/Resources/Localizations/sv.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/sv.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Licenser för öppen källkod"; "common.pinned" = "Fäst"; "common.send_to" = "Skicka till"; +"common.you" = "You"; "common_no_room_name" = "Inget rumsnamn"; "common_poll_end_confirmation" = "Är du säker på att du vill avsluta den här omröstningen?"; "common_poll_summary" = "Omröstning: %1$@"; diff --git a/ElementX/Resources/Localizations/uk.lproj/Localizable.strings b/ElementX/Resources/Localizations/uk.lproj/Localizable.strings index d0dad527bb..8e6abc1384 100644 --- a/ElementX/Resources/Localizations/uk.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/uk.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Ліцензії відкритого коду"; "common.pinned" = "Pinned"; "common.send_to" = "Надіслати до"; +"common.you" = "You"; "common_no_room_name" = "Немає назви кімнати"; "common_poll_end_confirmation" = "Ви впевнені, що хочете закінчити це опитування?"; "common_poll_summary" = "Опитування: %1$@"; diff --git a/ElementX/Resources/Localizations/uz.lproj/Localizable.strings b/ElementX/Resources/Localizations/uz.lproj/Localizable.strings index 7a9d54d95a..3aa76c9957 100644 --- a/ElementX/Resources/Localizations/uz.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/uz.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "Send to"; +"common.you" = "You"; "common_no_room_name" = "No room name"; "common_poll_end_confirmation" = "Haqiqatan ham bu soʻrovnomani tugatmoqchimisiz?"; "common_poll_summary" = "So‘rov:%1$@"; diff --git a/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings b/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings index a87e1e8a0b..7eea56e269 100644 --- a/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings @@ -27,7 +27,7 @@ "action_back" = "返回"; "action_call" = "呼叫"; "action_cancel" = "取消"; -"action_cancel_for_now" = "Cancel for now"; +"action_cancel_for_now" = "暂时取消"; "action_choose_photo" = "选择照片"; "action_clear" = "清除"; "action_close" = "关闭"; @@ -105,17 +105,17 @@ "action_tap_for_options" = "点按查看选项"; "action_try_again" = "再试一次"; "action_unpin" = "取消置顶"; -"action_view_in_timeline" = "View in timeline"; +"action_view_in_timeline" = "在时间轴中查看"; "action_view_source" = "查看源码"; "action_yes" = "是"; "action.load_more" = "载入更多"; "action_deactivate_account" = "Deactivate account"; -"banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; -"banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; -"banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; -"banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; -"banner.set_up_recovery.title" = "Set up recovery"; +"banner_migrate_to_native_sliding_sync_action" = "登出并升级"; +"banner_migrate_to_native_sliding_sync_description" = "您的服务器现在支持更快的新协议。现在登出并重新登录以进行升级。现在这样做可以帮助您避免在以后删除旧协议时被强制登出。"; +"banner_migrate_to_native_sliding_sync_force_logout_title" = "您的服务器不再支持旧协议。请登出并重新登录以继续使用此应用。"; +"banner_migrate_to_native_sliding_sync_title" = "有可用升级"; +"banner.set_up_recovery.content" = "生成新的恢复密钥,该密钥可用于在您无法访问设备时恢复加密的消息历史记录。"; +"banner.set_up_recovery.title" = "设置恢复"; "common_about" = "关于"; "common_acceptable_use_policy" = "可接受的使用政策"; "common_advanced_settings" = "高级设置"; @@ -229,6 +229,7 @@ "common.open_source_licenses" = "开源许可证"; "common.pinned" = "Pinned"; "common.send_to" = "发送至"; +"common.you" = "You"; "common_no_room_name" = "无房间名"; "common_poll_end_confirmation" = "确定要结束这个投票吗?"; "common_poll_summary" = "投票:%1$@"; @@ -299,7 +300,7 @@ "notification_room_invite_body" = "邀请你加入房间"; "notification_room_invite_body_with_sender" = "%1$@ invited you to join the room"; "notification_sender_me" = "我"; -"notification_sender_mention_reply" = "%1$@ mentioned or replied"; +"notification_sender_mention_reply" = "%1$@提及或回复"; "notification_test_push_notification_content" = "您正在查看通知!点击我!"; "notification_ticker_text_dm" = "%1$@:%2$@"; "notification_ticker_text_group" = "%1$@: %2$@ %3$@"; @@ -335,13 +336,13 @@ "screen_pinned_timeline_empty_state_description" = "按下消息并选择 “%1$@” 将其包含在此处。"; "screen_pinned_timeline_empty_state_headline" = "固定重要消息,以便轻松发现它们"; "screen_pinned_timeline_screen_title_empty" = "置顶消息"; -"screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; +"screen_reset_encryption_password_error" = "发生未知错误。请检查您的帐户密码是否正确,然后重试。"; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; "screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; "screen_resolve_send_failure_changed_identity_title" = "Your message was not sent because %1$@’s verified identity has changed"; "screen_resolve_send_failure_unsigned_device_primary_button_title" = "Send message anyway"; "screen_resolve_send_failure_unsigned_device_subtitle" = "%1$@ is using one or more unverified devices. You can send the message anyway, or you can cancel for now and try again later after %2$@ has verified all their devices."; -"screen_resolve_send_failure_unsigned_device_title" = "Your message was not sent because %1$@ has not verified all devices"; +"screen_resolve_send_failure_unsigned_device_title" = "您的消息未发送,因为%1$@尚未验证所有设备"; "screen_resolve_send_failure_you_unsigned_device_subtitle" = "One or more of your devices are unverified. You can send the message anyway, or you can cancel for now and try again later after you have verified all of your devices."; "screen_resolve_send_failure_you_unsigned_device_title" = "Your message was not sent because you have not verified one or more of your devices"; "screen_room_mentions_at_room_subtitle" = "通知整个房间"; @@ -351,7 +352,7 @@ "screen_room_pinned_banner_view_all_button_title" = "查看全部"; "screen_room_details_pinned_events_row_title" = "置顶消息"; "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; -"screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; +"screen_timeline_item_menu_send_failure_unsigned_device" = "消息未发送,因为%1$@尚未验证所有设备。"; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; "screen_account_provider_change" = "更改账户提供者"; "screen_account_provider_form_hint" = "服务器地址"; @@ -477,7 +478,7 @@ "screen_edit_profile_error_title" = "无法更新个人资料"; "screen_edit_profile_title" = "编辑个人资料"; "screen_edit_profile_updating_details" = "更新个人资料……"; -"screen_encryption_reset_action_continue_reset" = "Continue reset"; +"screen_encryption_reset_action_continue_reset" = "继续重置"; "screen_encryption_reset_bullet_1" = "您的账户信息、联系人、偏好设置和聊天列表将被保留"; "screen_encryption_reset_bullet_2" = "您将丢失现有的消息历史记录"; "screen_encryption_reset_bullet_3" = "您将需要再次验证所有您的现有设备和联系人"; @@ -639,8 +640,8 @@ "screen_reset_encryption_password_placeholder" = "输入..."; "screen_reset_encryption_password_subtitle" = "确认您要重置加密。"; "screen_reset_encryption_password_title" = "输入您的账户密码以继续"; -"screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; -"screen_reset_identity_confirmation_title" = "Can't confirm? Go to your account to reset your identity."; +"screen_reset_identity_confirmation_subtitle" = "您将要转到您的%1$@帐户来重置您的身份信息。之后,您将被带回该应用。"; +"screen_reset_identity_confirmation_title" = "无法确认?请前往您的帐户重置您的身份。"; "screen_room_alias_resolver_resolve_alias_failure" = "无法解析房间别名。"; "screen_room_attachment_source_camera" = "相机"; "screen_room_attachment_source_camera_video" = "录制视频"; diff --git a/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings b/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings index 42aeacb27e..886f7008ac 100644 --- a/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings @@ -229,6 +229,7 @@ "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "傳送給"; +"common.you" = "You"; "common_no_room_name" = "無聊天室名稱"; "common_poll_end_confirmation" = "您確定要結束這項投票嗎?"; "common_poll_summary" = "投票:%1$@"; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 23d1461ba7..29098b6e85 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -2419,6 +2419,8 @@ internal enum L10n { internal static var pinned: String { return L10n.tr("Localizable", "common.pinned") } /// Send to internal static var sendTo: String { return L10n.tr("Localizable", "common.send_to") } + /// You + internal static var you: String { return L10n.tr("Localizable", "common.you") } } } // swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length From be09f77ae2e23f87854efd7b233ec2b297578e91 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Fri, 27 Sep 2024 16:13:46 +0300 Subject: [PATCH 018/114] Switch the integration tests to the perf-only runner --- .github/workflows/integration-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index a884172491..fc2ec28bd3 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -9,7 +9,7 @@ on: jobs: integration_tests: name: Integration Tests - runs-on: macos-14 + runs-on: perf-only concurrency: # Only allow a single run of this workflow on each branch, automatically cancelling older runs. From a24e9932fc93f1b065046a8ebb2f8238bbaef1f6 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Fri, 27 Sep 2024 16:14:05 +0300 Subject: [PATCH 019/114] Re-enable the photo and document sharing flow tests --- IntegrationTests/Sources/UserFlowTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/IntegrationTests/Sources/UserFlowTests.swift b/IntegrationTests/Sources/UserFlowTests.swift index bd0be27d6c..46ae747e8d 100644 --- a/IntegrationTests/Sources/UserFlowTests.swift +++ b/IntegrationTests/Sources/UserFlowTests.swift @@ -41,9 +41,9 @@ class UserFlowTests: XCTestCase { sendMessages() - // Intentionally disabled as they're super flakey on iOS 18 simulators - // checkPhotoSharing() - // checkDocumentSharing() + checkPhotoSharing() + + checkDocumentSharing() checkLocationSharing() From bd3f80143bf7440f3cc4529040bc9fa55918700a Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Fri, 27 Sep 2024 17:49:14 +0300 Subject: [PATCH 020/114] Switch `codecov-Action@v3` to `v0.7.3` so it runs on Intel macs --- .github/workflows/integration-tests.yml | 1 + .github/workflows/ui_tests.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index fc2ec28bd3..6b753920e0 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -78,6 +78,7 @@ jobs: fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} flags: integrationtests + version: v0.7.3 - name: Collect test results if: ${{ !cancelled() }} diff --git a/.github/workflows/ui_tests.yml b/.github/workflows/ui_tests.yml index eab569396a..49c76e4e37 100644 --- a/.github/workflows/ui_tests.yml +++ b/.github/workflows/ui_tests.yml @@ -67,6 +67,7 @@ jobs: fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} flags: uitests + version: v0.7.3 - name: Collect test results if: ${{ !cancelled() }} From 5d35a74fb60efd5dee8d78089c2efa61c891e703 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 30 Sep 2024 11:13:14 +0100 Subject: [PATCH 021/114] Switch UI tests back to the perf-only runner. (#3349) --- .github/workflows/ui_tests.yml | 5 +---- ci_scripts/free_space.sh | 15 --------------- 2 files changed, 1 insertion(+), 19 deletions(-) delete mode 100755 ci_scripts/free_space.sh diff --git a/.github/workflows/ui_tests.yml b/.github/workflows/ui_tests.yml index 49c76e4e37..5fa202cb6f 100644 --- a/.github/workflows/ui_tests.yml +++ b/.github/workflows/ui_tests.yml @@ -13,7 +13,7 @@ on: jobs: tests: name: Tests - runs-on: macos-14 + runs-on: perf-only concurrency: # Only allow a single run of this workflow on each branch, automatically cancelling older runs. @@ -30,9 +30,6 @@ jobs: restore-keys: | ${{ runner.os }}-gems- - - name: Free disk space - run: ci_scripts/free_space.sh - - name: Setup environment run: source ci_scripts/ci_common.sh && setup_github_actions_environment diff --git a/ci_scripts/free_space.sh b/ci_scripts/free_space.sh deleted file mode 100755 index 18fdcc3351..0000000000 --- a/ci_scripts/free_space.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -# -# Taken from -# https://github.com/actions/runner-images/issues/2840#issuecomment-790492173 -# - -set -ux - -df -h -sudo rm -rf /usr/share/dotnet -sudo rm -rf /opt/ghc -sudo rm -rf "/usr/local/share/boost" -sudo rm -rf "$AGENT_TOOLSDIRECTORY" -df -h From 5f4c2890f6ee22d39cc19d7c8819a61ce10c23f5 Mon Sep 17 00:00:00 2001 From: Mauro <34335419+Velin92@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:32:35 +0200 Subject: [PATCH 022/114] Remove message pinning FF (#3318) --- .../Sources/Application/AppSettings.swift | 3 -- .../RoomDetailsScreenModels.swift | 1 - .../RoomDetailsScreenViewModel.swift | 9 ++---- .../View/RoomDetailsScreen.swift | 28 +++++++------------ .../Screens/RoomScreen/RoomScreenModels.swift | 3 +- .../RoomScreen/RoomScreenViewModel.swift | 11 ++------ .../DeveloperOptionsScreenModels.swift | 1 - .../View/DeveloperOptionsScreen.swift | 7 ----- .../Screens/Timeline/TimelineViewModel.swift | 7 ++--- .../Style/TimelineItemBubbledStylerView.swift | 6 ++-- .../Sources/Services/Client/ClientProxy.swift | 9 ++---- .../Sources/RoomScreenViewModelTests.swift | 2 -- .../Sources/TimelineViewModelTests.swift | 6 ---- 13 files changed, 21 insertions(+), 72 deletions(-) diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index d2985c8c75..3f8db72047 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -271,9 +271,6 @@ final class AppSettings { @UserPreference(key: UserDefaultsKeys.fuzzyRoomListSearchEnabled, defaultValue: false, storageType: .userDefaults(store)) var fuzzyRoomListSearchEnabled - @UserPreference(key: UserDefaultsKeys.pinningEnabled, defaultValue: true, storageType: .userDefaults(store)) - var pinningEnabled - enum SlidingSyncDiscovery: Codable { case proxy, native, forceNative } @UserPreference(key: UserDefaultsKeys.slidingSyncDiscovery, defaultValue: .native, storageType: .userDefaults(store)) var slidingSyncDiscovery: SlidingSyncDiscovery diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift index 72ff876c93..e1854f8a41 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift @@ -44,7 +44,6 @@ struct RoomDetailsScreenViewState: BindableState { var canEditRolesOrPermissions = false var notificationSettingsState: RoomDetailsNotificationSettingsState = .loading var canJoinCall = false - var isPinningEnabled = false var pinnedEventsActionState = RoomDetailsScreenPinnedEventsActionState.loading var canEdit: Bool { diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift index b435eeb232..55553d881f 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift @@ -75,13 +75,8 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr bindings: .init()), mediaProvider: mediaProvider) - appSettings.$pinningEnabled - .weakAssign(to: \.state.isPinningEnabled, on: self) - .store(in: &cancellables) - - appSettings.$pinningEnabled - .combineLatest(appMediator.networkMonitor.reachabilityPublisher) - .filter { $0.0 && $0.1 == .reachable } + appMediator.networkMonitor.reachabilityPublisher + .filter { $0 == .reachable } .receive(on: DispatchQueue.main) .sink { [weak self] _ in self?.setupPinnedEventsTimelineProviderIfNeeded() diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift b/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift index 3b682e8b26..919aa3aff8 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift @@ -189,15 +189,13 @@ struct RoomDetailsScreen: View { context.send(viewAction: .toggleFavourite(isFavourite: newValue)) } - if context.viewState.isPinningEnabled { - ListRow(label: .default(title: L10n.screenRoomDetailsPinnedEventsRowTitle, - icon: \.pin), - details: context.viewState.pinnedEventsActionState.isLoading ? .isWaiting(true) : .title(context.viewState.pinnedEventsActionState.count), - kind: context.viewState.pinnedEventsActionState.isLoading ? .label : .navigationLink(action: { - context.send(viewAction: .processTapPinnedEvents) - })) - .disabled(context.viewState.pinnedEventsActionState.isLoading) - } + ListRow(label: .default(title: L10n.screenRoomDetailsPinnedEventsRowTitle, + icon: \.pin), + details: context.viewState.pinnedEventsActionState.isLoading ? .isWaiting(true) : .title(context.viewState.pinnedEventsActionState.count), + kind: context.viewState.pinnedEventsActionState.isLoading ? .label : .navigationLink(action: { + context.send(viewAction: .processTapPinnedEvents) + })) + .disabled(context.viewState.pinnedEventsActionState.isLoading) if context.viewState.canEditRolesOrPermissions, context.viewState.dmRecipient == nil { ListRow(label: .default(title: L10n.screenRoomDetailsRolesAndPermissions, @@ -320,8 +318,6 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview { var notificationSettingsProxyMockConfiguration = NotificationSettingsProxyMockConfiguration() notificationSettingsProxyMockConfiguration.roomMode.isDefault = false let notificationSettingsProxy = NotificationSettingsProxyMock(with: notificationSettingsProxyMockConfiguration) - let appSettings = AppSettings() - appSettings.pinningEnabled = true return RoomDetailsScreenViewModel(roomProxy: roomProxy, clientProxy: ClientProxyMock(.init()), @@ -331,7 +327,7 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview { notificationSettingsProxy: notificationSettingsProxy, attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), appMediator: AppMediatorMock.default, - appSettings: appSettings) + appSettings: ServiceLocator.shared.settings) }() static let dmRoomViewModel = { @@ -348,8 +344,6 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview { canonicalAlias: "#alias:domain.com", members: members)) let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init()) - let appSettings = AppSettings() - appSettings.pinningEnabled = true return RoomDetailsScreenViewModel(roomProxy: roomProxy, clientProxy: ClientProxyMock(.init()), @@ -359,7 +353,7 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview { notificationSettingsProxy: notificationSettingsProxy, attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), appMediator: AppMediatorMock.default, - appSettings: appSettings) + appSettings: ServiceLocator.shared.settings) }() static let simpleRoomViewModel = { @@ -375,8 +369,6 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview { isEncrypted: false, members: members)) let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init()) - let appSettings = AppSettings() - appSettings.pinningEnabled = true return RoomDetailsScreenViewModel(roomProxy: roomProxy, clientProxy: ClientProxyMock(.init()), @@ -386,7 +378,7 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview { notificationSettingsProxy: notificationSettingsProxy, attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), appMediator: AppMediatorMock.default, - appSettings: appSettings) + appSettings: ServiceLocator.shared.settings) }() static var previews: some View { diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift index cd2d31f02c..9f4bb68a22 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift @@ -28,11 +28,10 @@ struct RoomScreenViewState: BindableState { var roomAvatar: RoomAvatar var lastScrollDirection: ScrollDirection? - var isPinningEnabled = false // This is used to control the banner var pinnedEventsBannerState: PinnedEventsBannerState = .loading(numbersOfEvents: 0) var shouldShowPinnedEventsBanner: Bool { - isPinningEnabled && !pinnedEventsBannerState.isEmpty && lastScrollDirection != .top + !pinnedEventsBannerState.isEmpty && lastScrollDirection != .top } var canJoinCall = false diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index 8c33e3e3ad..990d304081 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -124,15 +124,8 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } .store(in: &cancellables) - let pinningEnabledPublisher = appSettings.$pinningEnabled - - pinningEnabledPublisher - .weakAssign(to: \.state.isPinningEnabled, on: self) - .store(in: &cancellables) - - pinningEnabledPublisher - .combineLatest(appMediator.networkMonitor.reachabilityPublisher) - .filter { $0.0 && $0.1 == .reachable } + appMediator.networkMonitor.reachabilityPublisher + .filter { $0 == .reachable } .receive(on: DispatchQueue.main) .sink { [weak self] _ in self?.setupPinnedEventsTimelineProviderIfNeeded() diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift index 8a3d11809a..15b1f7d682 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift @@ -46,7 +46,6 @@ protocol DeveloperOptionsProtocol: AnyObject { var hideUnreadMessagesBadge: Bool { get set } var elementCallBaseURLOverride: URL? { get set } var fuzzyRoomListSearchEnabled: Bool { get set } - var pinningEnabled: Bool { get set } var enableOnlySignedDeviceIsolationMode: Bool { get set } } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift index 30f6e8ec87..723ae359d5 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -35,13 +35,6 @@ struct DeveloperOptionsScreen: View { Text(context.viewState.slidingSyncFooter) } - Section("Message Pinning") { - Toggle(isOn: $context.pinningEnabled) { - Text("Enable message pinning") - Text("Requires app reboot") - } - } - Section("Room List") { Toggle(isOn: $context.hideUnreadMessagesBadge) { Text("Hide grey dots") diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index 623b5d4121..ffc0386f20 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -336,8 +336,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { state.canCurrentUserRedactSelf = false } - if appSettings.pinningEnabled, - case let .success(value) = await roomProxy.canUserPinOrUnpin(userID: roomProxy.ownUserID) { + if case let .success(value) = await roomProxy.canUserPinOrUnpin(userID: roomProxy.ownUserID) { state.canCurrentUserPin = value } else { state.canCurrentUserPin = false @@ -444,9 +443,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { } private func updatePinnedEventIDs() async { - if appSettings.pinningEnabled { - state.pinnedEventIDs = await roomProxy.pinnedEventIDs - } + state.pinnedEventIDs = await roomProxy.pinnedEventIDs } private func setupDirectRoomSubscriptionsIfNeeded() { diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift index 32b33c57a8..49ca3828bd 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift @@ -355,8 +355,6 @@ private extension View { struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview { static let viewModel = TimelineViewModel.mock static let viewModelWithPins: TimelineViewModel = { - var settings = AppSettings() - settings.pinningEnabled = true let roomProxy = JoinedRoomProxyMock(.init(name: "Preview Room", pinnedEventIDs: [""])) return TimelineViewModel(roomProxy: roomProxy, focussedEventID: nil, @@ -366,7 +364,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, appMediator: AppMediatorMock.default, - appSettings: settings, + appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics) }() @@ -384,7 +382,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview .previewDisplayName("Encryption Indicators") pinned .previewDisplayName("Pinned messages") - .snapshotPreferences(delay: 1.0) + .snapshotPreferences(delay: 2.0) } // These always include a reply diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index b06c3e0cee..9a05854447 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -816,12 +816,13 @@ class ClientProxy: ClientProxyProtocol { }) } - private lazy var eventFilters: TimelineEventTypeFilter = { + private let eventFilters: TimelineEventTypeFilter = { var stateEventFilters: [StateEventType] = [.roomAliases, .roomCanonicalAlias, .roomGuestAccess, .roomHistoryVisibility, .roomJoinRules, + .roomPinnedEvents, .roomPowerLevels, .roomServerAcl, .roomTombstone, @@ -830,12 +831,6 @@ class ClientProxy: ClientProxyProtocol { .policyRuleRoom, .policyRuleServer, .policyRuleUser] - - // Reminder: once the feature flag is not required anymore, change the lazy var back to a let - if !appSettings.pinningEnabled { - stateEventFilters.append(.roomPinnedEvents) - } - return .exclude(eventTypes: stateEventFilters.map { FilterTimelineEventType.state(eventType: $0) }) }() diff --git a/UnitTests/Sources/RoomScreenViewModelTests.swift b/UnitTests/Sources/RoomScreenViewModelTests.swift index a7998b4dce..ab52a3cd1c 100644 --- a/UnitTests/Sources/RoomScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomScreenViewModelTests.swift @@ -23,7 +23,6 @@ class RoomScreenViewModelTests: XCTestCase { } func testPinnedEventsBanner() async throws { - ServiceLocator.shared.settings.pinningEnabled = true let timelineSubject = PassthroughSubject() let updateSubject = PassthroughSubject() let roomProxyMock = JoinedRoomProxyMock(.init()) @@ -101,7 +100,6 @@ class RoomScreenViewModelTests: XCTestCase { } func testPinnedEventsBannerSelection() async throws { - ServiceLocator.shared.settings.pinningEnabled = true let roomProxyMock = JoinedRoomProxyMock(.init()) // setup a way to inject the mock of the pinned events timeline let pinnedTimelineMock = TimelineProxyMock() diff --git a/UnitTests/Sources/TimelineViewModelTests.swift b/UnitTests/Sources/TimelineViewModelTests.swift index 22e63a25ab..1243d52918 100644 --- a/UnitTests/Sources/TimelineViewModelTests.swift +++ b/UnitTests/Sources/TimelineViewModelTests.swift @@ -346,9 +346,6 @@ class TimelineViewModelTests: XCTestCase { // MARK: - Pins func testPinnedEvents() async throws { - ServiceLocator.shared.settings.pinningEnabled = true - - // Note: We need to start the test with a non-default value so we know the view model has finished the Task. let roomProxyMock = JoinedRoomProxyMock(.init(name: "", pinnedEventIDs: .init(["test1"]))) let actionsSubject = PassthroughSubject() @@ -378,9 +375,6 @@ class TimelineViewModelTests: XCTestCase { } func testCanUserPinEvents() async throws { - ServiceLocator.shared.settings.pinningEnabled = true - - // Note: We need to start the test with the non-default value so we know the view model has finished the Task. let roomProxyMock = JoinedRoomProxyMock(.init(name: "", canUserPin: true)) let actionsSubject = PassthroughSubject() roomProxyMock.underlyingActionsPublisher = actionsSubject.eraseToAnyPublisher() From 268d9f74795d10440408ea9b27de86702ec9b0f1 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Tue, 1 Oct 2024 13:09:45 +0100 Subject: [PATCH 023/114] Move the core logic in LoginScreenCoordinator into the ViewModel. (#3348) --- ElementX.xcodeproj/project.pbxproj | 16 +- .../AuthenticationFlowCoordinator.swift | 5 +- .../LoginScreen/LoginMode.swift | 6 +- .../LoginScreen/LoginScreenCoordinator.swift | 119 ++---------- .../LoginScreen/LoginScreenModels.swift | 33 ++-- .../LoginScreen/LoginScreenViewModel.swift | 126 ++++++++++--- .../LoginScreenViewModelProtocol.swift | 13 +- .../LoginScreen/View/LoginScreen.swift | 62 ++++--- .../ServerConfirmationScreenViewModel.swift | 6 +- .../AuthenticationService.swift | 12 +- ...nScreen-iPad-en-GB.Credentials-Entered.png | 4 +- ...t_loginScreen-iPad-en-GB.OIDC-Fallback.png | 3 - ...est_loginScreen-iPad-en-GB.Unsupported.png | 4 +- ...test_loginScreen-iPad-en-GB.matrix-org.png | 4 +- ...Screen-iPad-pseudo.Credentials-Entered.png | 4 +- ..._loginScreen-iPad-pseudo.OIDC-Fallback.png | 3 - ...st_loginScreen-iPad-pseudo.Unsupported.png | 4 +- ...est_loginScreen-iPad-pseudo.matrix-org.png | 4 +- ...en-iPhone-16-en-GB.Credentials-Entered.png | 4 +- ...inScreen-iPhone-16-en-GB.OIDC-Fallback.png | 3 - ...oginScreen-iPhone-16-en-GB.Unsupported.png | 4 +- ...loginScreen-iPhone-16-en-GB.matrix-org.png | 4 +- ...n-iPhone-16-pseudo.Credentials-Entered.png | 4 +- ...nScreen-iPhone-16-pseudo.OIDC-Fallback.png | 3 - ...ginScreen-iPhone-16-pseudo.Unsupported.png | 4 +- ...oginScreen-iPhone-16-pseudo.matrix-org.png | 4 +- .../Sources/LoginScreenViewModelTests.swift | 173 ++++++++++++++++++ UnitTests/Sources/LoginViewModelTests.swift | 137 -------------- ...ServerSelectionScreenViewModelTests.swift} | 2 +- 29 files changed, 382 insertions(+), 388 deletions(-) delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.OIDC-Fallback.png delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.OIDC-Fallback.png delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.OIDC-Fallback.png delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.OIDC-Fallback.png create mode 100644 UnitTests/Sources/LoginScreenViewModelTests.swift delete mode 100644 UnitTests/Sources/LoginViewModelTests.swift rename UnitTests/Sources/{ServerSelectionViewModelTests.swift => ServerSelectionScreenViewModelTests.swift} (99%) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index fc5cba257d..c4c8ffe5a9 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -144,7 +144,6 @@ 1D623953F970D11F6F38499C /* AppLockService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851B95BB98649B8E773D6790 /* AppLockService.swift */; }; 1D69E31913DF66426985909B /* EmojiPickerScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11151E78D6BB2B04A8FBD389 /* EmojiPickerScreenViewModelProtocol.swift */; }; 1DC227816777A2F3A19657E5 /* RoomDirectorySearchScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCF71646898A2F720C5BFDF5 /* RoomDirectorySearchScreenViewModel.swift */; }; - 1E59B77A0B2CE83DCC1B203C /* LoginViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A05707BF550D770168A406DB /* LoginViewModelTests.swift */; }; 1F3232BD368DF430AB433907 /* Compound in Frameworks */ = {isa = PBXBuildFile; productRef = 07FEEEDB11543A7DED420F04 /* Compound */; }; 1FE593ECEC40A43789105D80 /* KeychainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E36CB905A2B9EC2C92A2DA7C /* KeychainController.swift */; }; 1FEC0A4EC6E6DF693C16B32A /* StringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CEBCB9676FCD1D0F13188DD /* StringTests.swift */; }; @@ -463,6 +462,7 @@ 67C05C50AD734283374605E3 /* MatrixEntityRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */; }; 67D6E0700A9C1E676F6231F8 /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = AD544C0FA48DFFB080920061 /* Collections */; }; 67E9926C4572C54F59FCA91A /* AuthenticationFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9B069D7772DDF6513E0F1B8 /* AuthenticationFlowCoordinator.swift */; }; + 67ECD32538F6DAFE38A623F9 /* ServerSelectionScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E45EBAFF1A83538D54ABDF92 /* ServerSelectionScreenViewModelTests.swift */; }; 67EFF46180B939CBF389AECD /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93C713D124FE915ABF47A6B7 /* TimelineView.swift */; }; 6817EAD73DC1FFD8B943B5B9 /* HomeScreenRoomTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B73587C2E3CF5998361AE516 /* HomeScreenRoomTests.swift */; }; 68184EF36396424FE19A727D /* MediaLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AFCE895ECFFA53FEE64D62B /* MediaLoader.swift */; }; @@ -513,6 +513,7 @@ 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 */; }; + 7434A7F02D587A920B376A9A /* LoginScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A43964330459965AF048A8C /* LoginScreenViewModelTests.swift */; }; 743790BF6A5B0577EA74AF14 /* ReadMarkerRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF3D25B3EDB283B5807EADCF /* ReadMarkerRoomTimelineItem.swift */; }; 748F482FEF4E04D61C39AAD7 /* EmojiPickerScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = F174A5627CDB3CAF280D1880 /* EmojiPickerScreenModels.swift */; }; 7501442D52A65F73DF79FFD4 /* PaginationIndicatorRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B987FC3FDBAA0E1C5AA235C /* PaginationIndicatorRoomTimelineItem.swift */; }; @@ -668,7 +669,6 @@ 92D9088B901CEBB1A99ECA4E /* RoomMemberProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36FD673E24FBFCFDF398716A /* RoomMemberProxyMock.swift */; }; 934051B17A884AB0635DF81B /* BlockedUsersScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A010B8EAD1A9F6B4686DF2F4 /* BlockedUsersScreenViewModel.swift */; }; 937985546F708339711ECDFC /* ComposerToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85666E40F7E817809B4FD787 /* ComposerToolbar.swift */; }; - 93875ADD456142D20823ED24 /* ServerSelectionViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDAA4472821985BF868CC21C /* ServerSelectionViewModelTests.swift */; }; 93A549135E6C027A0D823BFE /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 593FBBF394712F2963E98A0B /* DTCoreText */; }; 93AC1E8418D8C827671FB3A9 /* IdentityConfirmedScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 595EC503DA5517BBE6D39406 /* IdentityConfirmedScreenCoordinator.swift */; }; 93BA4A81B6D893271101F9F0 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = A7CA6F33C553805035C3B114 /* DeviceKit */; }; @@ -1607,6 +1607,7 @@ 5A1119E9C63AE530252640D2 /* SecureBackupController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupController.swift; sourceTree = ""; }; 5A2FCA3D0F239B9E911B966B /* SecureBackupRecoveryKeyScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupRecoveryKeyScreen.swift; sourceTree = ""; }; 5A37E2FACFD041CE466223CD /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 5A43964330459965AF048A8C /* LoginScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenViewModelTests.swift; sourceTree = ""; }; 5AEA0B743847CFA5B3C38EE4 /* RoomMembersListScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListScreenCoordinator.swift; sourceTree = ""; }; 5B8F0ED874DF8C9A51B0AB6F /* SettingsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenCoordinator.swift; sourceTree = ""; }; 5C7C7CFA6B2A62A685FF6CE3 /* DeveloperOptionsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreenCoordinator.swift; sourceTree = ""; }; @@ -1888,7 +1889,6 @@ A010B8EAD1A9F6B4686DF2F4 /* BlockedUsersScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedUsersScreenViewModel.swift; sourceTree = ""; }; A019A12C866D64CF072024B9 /* AppLockSetupPINScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenViewModel.swift; sourceTree = ""; }; A02D1A490944BF01A37586E1 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/SAS.strings; sourceTree = ""; }; - A05707BF550D770168A406DB /* LoginViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewModelTests.swift; sourceTree = ""; }; A057F2FDC14866C3026A89A4 /* NotificationManagerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManagerProtocol.swift; sourceTree = ""; }; A12D3B1BCF920880CA8BBB6B /* UserIndicatorControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorControllerProtocol.swift; sourceTree = ""; }; A130A2251A15A7AACC84FD37 /* RoomPollsHistoryScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreenViewModelProtocol.swift; sourceTree = ""; }; @@ -2186,6 +2186,7 @@ E413F4CBD7BF0588F394A9DD /* RoomDetailsEditScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreenViewModel.swift; sourceTree = ""; }; E43005941B3A2C9671E23C85 /* UserIndicatorModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorModalView.swift; sourceTree = ""; }; E44E35AA87F49503E7B3BF6E /* AudioConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioConverter.swift; sourceTree = ""; }; + E45EBAFF1A83538D54ABDF92 /* ServerSelectionScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionScreenViewModelTests.swift; sourceTree = ""; }; E461B3C8BBBFCA400B268D14 /* AppRouteURLParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouteURLParserTests.swift; sourceTree = ""; }; E5272BC4A60B6AD7553BACA1 /* BlurHashDecode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashDecode.swift; sourceTree = ""; }; E53BFB7E4F329621C844E8C3 /* AnalyticsPromptScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptScreen.swift; sourceTree = ""; }; @@ -2233,7 +2234,6 @@ ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; path = message.caf; sourceTree = ""; }; ED49073BB1C1FC649DAC2CCD /* LocationRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationRoomTimelineView.swift; sourceTree = ""; }; ED60E4D2CD678E1EBF16F77A /* BlockedUsersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedUsersScreen.swift; sourceTree = ""; }; - EDAA4472821985BF868CC21C /* ServerSelectionViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionViewModelTests.swift; sourceTree = ""; }; EE378083653EF0C9B5E9D580 /* EmoteRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmoteRoomTimelineItemContent.swift; sourceTree = ""; }; EE6BFF453838CF6C3982C5A3 /* RoomDirectorySearchScreenScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreenScreenViewModelTests.swift; sourceTree = ""; }; EEAA2832D93EC7D2608703FB /* NSEUserSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEUserSession.swift; sourceTree = ""; }; @@ -3828,7 +3828,7 @@ 6E5725BC6C63604CB769145B /* LegalInformationScreenViewModelTests.swift */, C070FD43DC6BF4E50217965A /* LocalizationTests.swift */, 3DC1943ADE6A62ED5129D7C8 /* LoggingTests.swift */, - A05707BF550D770168A406DB /* LoginViewModelTests.swift */, + 5A43964330459965AF048A8C /* LoginScreenViewModelTests.swift */, 376D941BF8BB294389C0DE24 /* MapTilerURLBuildersTests.swift */, F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */, 2D7A2C4A3A74F0D2FFE9356A /* MediaPlayerProviderTests.swift */, @@ -3870,7 +3870,7 @@ 40316EFFEAC7B206EE9A55AE /* SecureBackupScreenViewModelTests.swift */, 277C20CDD5B64510401B6D0D /* ServerConfigurationScreenViewStateTests.swift */, F08776C48FFB47CACF64ED10 /* ServerConfirmationScreenViewModelTests.swift */, - EDAA4472821985BF868CC21C /* ServerSelectionViewModelTests.swift */, + E45EBAFF1A83538D54ABDF92 /* ServerSelectionScreenViewModelTests.swift */, 0825EAFD47332DD459DE893F /* SessionDirectoriesTests.swift */, A1C22B1B5FA3A765EADB2CC9 /* SessionVerificationStateMachineTests.swift */, DF05DA24F71B455E8EFEBC3B /* SessionVerificationViewModelTests.swift */, @@ -6122,7 +6122,7 @@ 8AC256AF0EC54658321C9241 /* LegalInformationScreenViewModelTests.swift in Sources */, 0033481EE363E4914295F188 /* LocalizationTests.swift in Sources */, 149D1942DC005D0485FB8D93 /* LoggingTests.swift in Sources */, - 1E59B77A0B2CE83DCC1B203C /* LoginViewModelTests.swift in Sources */, + 7434A7F02D587A920B376A9A /* LoginScreenViewModelTests.swift in Sources */, 77C1A2F49CD90D3EFDF376E5 /* MapTilerURLBuildersTests.swift in Sources */, 2E43A3D221BE9587BC19C3F1 /* MatrixEntityRegexTests.swift in Sources */, 4B978C09567387EF4366BD7A /* MediaLoaderTests.swift in Sources */, @@ -6171,7 +6171,7 @@ 1B8E30B35BF8F541C1318F19 /* SecureBackupScreenViewModelTests.swift in Sources */, 53A55748D5F587C9061F98BF /* ServerConfigurationScreenViewStateTests.swift in Sources */, 89658A44C9FC19B58FD1C226 /* ServerConfirmationScreenViewModelTests.swift in Sources */, - 93875ADD456142D20823ED24 /* ServerSelectionViewModelTests.swift in Sources */, + 67ECD32538F6DAFE38A623F9 /* ServerSelectionScreenViewModelTests.swift in Sources */, CC1C948F67A5510A340FD7F0 /* SessionDirectoriesTests.swift in Sources */, 86675910612A12409262DFBD /* SessionVerificationStateMachineTests.swift in Sources */, 755727E0B756430DFFEC4732 /* SessionVerificationViewModelTests.swift in Sources */, diff --git a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift index f2cf5c9f47..9425920ede 100644 --- a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift @@ -244,8 +244,9 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { private func showLoginScreen() { let parameters = LoginScreenCoordinatorParameters(authenticationService: authenticationService, - analytics: analytics, - userIndicatorController: userIndicatorController) + slidingSyncLearnMoreURL: appSettings.slidingSyncLearnMoreURL, + userIndicatorController: userIndicatorController, + analytics: analytics) let coordinator = LoginScreenCoordinator(parameters: parameters) coordinator.actions diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginMode.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginMode.swift index 775b7eedc4..9d7465355e 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginMode.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginMode.swift @@ -20,10 +20,8 @@ enum LoginMode: Equatable { var supportsOIDCFlow: Bool { switch self { - case .oidc: - return true - default: - return false + case .oidc: true + default: false } } } diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenCoordinator.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenCoordinator.swift index fbbb7409ba..66e9524750 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenCoordinator.swift @@ -11,9 +11,9 @@ import SwiftUI struct LoginScreenCoordinatorParameters { /// The service used to authenticate the user. let authenticationService: AuthenticationServiceProtocol - - let analytics: AnalyticsService + let slidingSyncLearnMoreURL: URL let userIndicatorController: UserIndicatorControllerProtocol + let analytics: AnalyticsService } enum LoginScreenCoordinatorAction { @@ -42,8 +42,10 @@ final class LoginScreenCoordinator: CoordinatorProtocol { init(parameters: LoginScreenCoordinatorParameters) { self.parameters = parameters - viewModel = LoginScreenViewModel(homeserver: parameters.authenticationService.homeserver.value, - slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL) + viewModel = LoginScreenViewModel(authenticationService: parameters.authenticationService, + slidingSyncLearnMoreURL: parameters.slidingSyncLearnMoreURL, + userIndicatorController: parameters.userIndicatorController, + analytics: parameters.analytics) } // MARK: - Public @@ -54,119 +56,20 @@ final class LoginScreenCoordinator: CoordinatorProtocol { guard let self else { return } switch action { - case .parseUsername(let username): - parseUsername(username) - case .forgotPassword: - showForgotPasswordScreen() - case .login(let username, let password): - login(username: username, password: password) + case .configuredForOIDC: + actionsSubject.send(.configuredForOIDC) + case .signedIn(let userSession): + actionsSubject.send(.signedIn(userSession)) } } .store(in: &cancellables) } func stop() { - stopLoading() + viewModel.stopLoading() } func toPresentable() -> AnyView { AnyView(LoginScreen(context: viewModel.context)) } - - // MARK: - Private - - private static let loadingIndicatorIdentifier = "\(LoginScreenCoordinatorAction.self)-Loading" - - private func startLoading(isInteractionBlocking: Bool) { - if isInteractionBlocking { - parameters.userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, - type: .modal, - title: L10n.commonLoading, - persistent: true)) - } else { - viewModel.update(isLoading: true) - } - } - - private func stopLoading() { - viewModel.update(isLoading: false) - parameters.userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier) - } - - /// Processes an error to either update the flow or display it to the user. - private func handleError(_ error: AuthenticationServiceError) { - MXLog.info("Error occurred: \(error)") - - switch error { - case .invalidCredentials: - viewModel.displayError(.alert(L10n.screenLoginErrorInvalidCredentials)) - case .accountDeactivated: - viewModel.displayError(.alert(L10n.screenLoginErrorDeactivatedAccount)) - case .invalidWellKnown(let error): - viewModel.displayError(.invalidWellKnownAlert(error)) - case .slidingSyncNotAvailable: - viewModel.displayError(.slidingSyncAlert) - case .sessionTokenRefreshNotSupported: - viewModel.displayError(.refreshTokenAlert) - default: - viewModel.displayError(.alert(L10n.errorUnknown)) - } - } - - /// Requests the authentication coordinator to log in using the specified credentials. - private func login(username: String, password: String) { - MXLog.info("Starting login with password.") - startLoading(isInteractionBlocking: true) - - Task { - parameters.analytics.signpost.beginLogin() - switch await authenticationService.login(username: username, - password: password, - initialDeviceName: UIDevice.current.initialDeviceName, - deviceID: nil) { - case .success(let userSession): - actionsSubject.send(.signedIn(userSession)) - parameters.analytics.signpost.endLogin() - stopLoading() - case .failure(let error): - stopLoading() - parameters.analytics.signpost.endLogin() - handleError(error) - } - } - } - - /// Parses the specified username and looks up the homeserver when a Matrix ID is entered. - private func parseUsername(_ username: String) { - guard MatrixEntityRegex.isMatrixUserIdentifier(username) else { return } - - let homeserverDomain = String(username.split(separator: ":")[1]) - - startLoading(isInteractionBlocking: false) - - Task { - switch await authenticationService.configure(for: homeserverDomain, flow: .login) { - case .success: - stopLoading() - if authenticationService.homeserver.value.loginMode == .oidc { - actionsSubject.send(.configuredForOIDC) - } else { - updateViewModel() - } - case .failure(let error): - stopLoading() - handleError(error) - } - } - } - - /// Updates the view model with a different homeserver. - private func updateViewModel() { - viewModel.update(homeserver: authenticationService.homeserver.value) - } - - /// Shows the forgot password screen. - private func showForgotPasswordScreen() { - viewModel.displayError(.alert("Not implemented.")) - } } diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenModels.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenModels.swift index e9c80169aa..b7979bd7c6 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenModels.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenModels.swift @@ -7,23 +7,16 @@ import Foundation -enum LoginScreenViewModelAction: CustomStringConvertible { - /// Parse the username and update the homeserver if included. - case parseUsername(String) - /// The user would like to reset their password. - case forgotPassword - /// Login using the supplied credentials. - case login(username: String, password: String) +enum LoginScreenViewModelAction { + /// The homeserver was updated to one that supports OIDC. + case configuredForOIDC + /// Login was successful. + case signedIn(UserSessionProtocol) - /// A string representation of the action, ignoring any associated values that could leak PII. - var description: String { + var isConfiguredForOIDC: Bool { switch self { - case .parseUsername: - return "parseUsername" - case .forgotPassword: - return "forgotPassword" - case .login: - return "login" + case .configuredForOIDC: true + default: false } } } @@ -34,7 +27,7 @@ struct LoginScreenViewState: BindableState { /// Whether a new homeserver is currently being loaded. var isLoading = false /// View state that can be bound to from SwiftUI. - var bindings: LoginScreenBindings + var bindings = LoginScreenBindings() /// The types of login supported by the homeserver. var loginMode: LoginMode { homeserver.loginMode } @@ -62,8 +55,6 @@ struct LoginScreenBindings { enum LoginScreenViewAction { /// Parse the username to detect if a homeserver is included. case parseUsername - /// The user would like to reset their password. - case forgotPassword /// Continue using the input username and password. case next } @@ -71,8 +62,10 @@ enum LoginScreenViewAction { enum LoginScreenErrorType: Hashable { /// A specific error message shown in an alert. case alert(String) - /// Looking up the homeserver from the username failed. - case invalidHomeserver + /// An alert that informs the user to check their username/password. + case credentialsAlert + /// An alert that informs the user that their account has been deactivated. + case deactivatedAlert /// An alert that informs the user about a bad well-known file. case invalidWellKnownAlert(String) /// An alert that allows the user to learn about sliding sync. diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenViewModel.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenViewModel.swift index 0c81c9e412..3935c8e309 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenViewModel.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenViewModel.swift @@ -11,57 +11,129 @@ import SwiftUI typealias LoginScreenViewModelType = StateStoreViewModel class LoginScreenViewModel: LoginScreenViewModelType, LoginScreenViewModelProtocol { + private let authenticationService: AuthenticationServiceProtocol private let slidingSyncLearnMoreURL: URL + private let userIndicatorController: UserIndicatorControllerProtocol + private let analytics: AnalyticsService private var actionsSubject: PassthroughSubject = .init() - var actions: AnyPublisher { actionsSubject.eraseToAnyPublisher() } - init(homeserver: LoginHomeserver, slidingSyncLearnMoreURL: URL) { + init(authenticationService: AuthenticationServiceProtocol, + slidingSyncLearnMoreURL: URL, + userIndicatorController: UserIndicatorControllerProtocol, + analytics: AnalyticsService) { + self.authenticationService = authenticationService self.slidingSyncLearnMoreURL = slidingSyncLearnMoreURL - let bindings = LoginScreenBindings() - let viewState = LoginScreenViewState(homeserver: homeserver, bindings: bindings) + self.userIndicatorController = userIndicatorController + self.analytics = analytics + + let viewState = LoginScreenViewState(homeserver: authenticationService.homeserver.value) super.init(initialViewState: viewState) + + authenticationService.homeserver + .receive(on: DispatchQueue.main) + .weakAssign(to: \.state.homeserver, on: self) + .store(in: &cancellables) } override func process(viewAction: LoginScreenViewAction) { switch viewAction { case .parseUsername: - actionsSubject.send(.parseUsername(state.bindings.username)) - case .forgotPassword: - actionsSubject.send(.forgotPassword) + parseUsername() case .next: - actionsSubject.send(.login(username: state.bindings.username, password: state.bindings.password)) + login() + } + } + + func stopLoading() { + state.isLoading = false + userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier) + } + + // MARK: - Private + + /// Parses the specified username and looks up the homeserver when a Matrix ID is entered. + private func parseUsername() { + let username = state.bindings.username + + guard MatrixEntityRegex.isMatrixUserIdentifier(username) else { return } + + let homeserverDomain = String(username.split(separator: ":")[1]) + + startLoading(isInteractionBlocking: false) + + Task { + switch await authenticationService.configure(for: homeserverDomain, flow: .login) { + case .success: + if authenticationService.homeserver.value.loginMode == .oidc { + actionsSubject.send(.configuredForOIDC) + } + stopLoading() + case .failure(let error): + stopLoading() + handleError(error) + } } } - func update(isLoading: Bool) { - guard state.isLoading != isLoading else { return } - state.isLoading = isLoading + /// Requests the authentication coordinator to log in using the specified credentials. + private func login() { + MXLog.info("Starting login with password.") + startLoading(isInteractionBlocking: true) + + Task { + analytics.signpost.beginLogin() + switch await authenticationService.login(username: state.bindings.username, + password: state.bindings.password, + initialDeviceName: UIDevice.current.initialDeviceName, + deviceID: nil) { + case .success(let userSession): + actionsSubject.send(.signedIn(userSession)) + analytics.signpost.endLogin() + stopLoading() + case .failure(let error): + stopLoading() + analytics.signpost.endLogin() + handleError(error) + } + } } - func update(homeserver: LoginHomeserver) { - state.homeserver = homeserver + private static let loadingIndicatorIdentifier = "\(LoginScreenCoordinatorAction.self)-Loading" + + private func startLoading(isInteractionBlocking: Bool) { + if isInteractionBlocking { + userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, + type: .modal, + title: L10n.commonLoading, + persistent: true)) + } else { + state.isLoading = true + } } - func displayError(_ type: LoginScreenErrorType) { - switch type { - case .alert(let message): - state.bindings.alertInfo = AlertInfo(id: type, + /// Processes an error to either update the flow or display it to the user. + private func handleError(_ error: AuthenticationServiceError) { + MXLog.info("Error occurred: \(error)") + + switch error { + case .invalidCredentials: + state.bindings.alertInfo = AlertInfo(id: .credentialsAlert, title: L10n.commonError, - message: message) - case .invalidHomeserver: - state.bindings.alertInfo = AlertInfo(id: type, + message: L10n.screenLoginErrorInvalidCredentials) + case .accountDeactivated: + state.bindings.alertInfo = AlertInfo(id: .deactivatedAlert, title: L10n.commonError, - message: L10n.screenLoginErrorInvalidUserId) - case .invalidWellKnownAlert(let error): + message: L10n.screenLoginErrorDeactivatedAccount) + case .invalidWellKnown(let error): state.bindings.alertInfo = AlertInfo(id: .slidingSyncAlert, title: L10n.commonServerNotSupported, message: L10n.screenChangeServerErrorInvalidWellKnown(error)) - case .slidingSyncAlert: + case .slidingSyncNotAvailable: let openURL = { UIApplication.shared.open(self.slidingSyncLearnMoreURL) } state.bindings.alertInfo = AlertInfo(id: .slidingSyncAlert, title: L10n.commonServerNotSupported, @@ -71,12 +143,12 @@ class LoginScreenViewModel: LoginScreenViewModelType, LoginScreenViewModelProtoc // Clear out the invalid username to avoid an attempted login to matrix.org state.bindings.username = "" - case .refreshTokenAlert: - state.bindings.alertInfo = AlertInfo(id: type, + case .sessionTokenRefreshNotSupported: + state.bindings.alertInfo = AlertInfo(id: .refreshTokenAlert, title: L10n.commonServerNotSupported, message: L10n.screenLoginErrorRefreshTokens) - case .unknown: - state.bindings.alertInfo = AlertInfo(id: type) + default: + state.bindings.alertInfo = AlertInfo(id: .unknown) } } } diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenViewModelProtocol.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenViewModelProtocol.swift index 303c6151c9..cccafd219c 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenViewModelProtocol.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/LoginScreenViewModelProtocol.swift @@ -12,15 +12,6 @@ protocol LoginScreenViewModelProtocol { var actions: AnyPublisher { get } var context: LoginScreenViewModelType.Context { get } - /// Update the view to reflect that a new homeserver is being loaded. - /// - Parameter isLoading: Whether or not the homeserver is being loaded. - func update(isLoading: Bool) - - /// Update the view with new homeserver information. - /// - Parameter homeserver: The view data for the homeserver. This can be generated using `AuthenticationService.Homeserver.viewData`. - func update(homeserver: LoginHomeserver) - - /// Display an error to the user. - /// - Parameter type: The type of error to be displayed. - func displayError(_ type: LoginScreenErrorType) + /// Update the view to reflect that loaded has finished. + func stopLoading() } diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginScreen.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginScreen.swift index 69408cd759..1610591e65 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginScreen.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginScreen.swift @@ -29,6 +29,7 @@ struct LoginScreen: View { // This should never be shown. ProgressView() default: + // This should never be shown either. loginUnavailableText } } @@ -37,6 +38,7 @@ struct LoginScreen: View { .padding(.bottom, 16) } .background(Color.compound.bgCanvasDefault.ignoresSafeArea()) + .navigationBarTitleDisplayMode(.inline) .alert(item: $context.alertInfo) } @@ -124,35 +126,45 @@ struct LoginScreen: View { // MARK: - Previews struct LoginScreen_Previews: PreviewProvider, TestablePreview { - static let credentialsViewModel: LoginScreenViewModel = { - let viewModel = LoginScreenViewModel(homeserver: .mockMatrixDotOrg, slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL) - viewModel.context.username = "alice" - viewModel.context.password = "password" - return viewModel - }() + static let viewModel = makeViewModel() + static let credentialsViewModel = makeViewModel(withCredentials: true) + static let unconfiguredViewModel = makeViewModel(homeserverAddress: "somethingtofailconfiguration") static var previews: some View { - screen(for: LoginScreenViewModel(homeserver: .mockMatrixDotOrg, slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL)) - .previewDisplayName("matrix.org") - screen(for: credentialsViewModel) - .previewDisplayName("Credentials Entered") - screen(for: LoginScreenViewModel(homeserver: .mockMatrixDotOrg, slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL)) - .previewDisplayName("Unsupported") - screen(for: LoginScreenViewModel(homeserver: .mockMatrixDotOrg, slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL)) - .previewDisplayName("OIDC Fallback") - } - - static func screen(for viewModel: LoginScreenViewModel) -> some View { NavigationStack { LoginScreen(context: viewModel.context) - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .cancellationAction) { - Button { } label: { - Text("\(Image(systemName: "chevron.backward")) Back") - } - } - } } + .previewDisplayName("matrix.org") + .snapshotPreferences(delay: 0.1) + + NavigationStack { + LoginScreen(context: credentialsViewModel.context) + } + .previewDisplayName("Credentials Entered") + .snapshotPreferences(delay: 0.1) + + NavigationStack { + LoginScreen(context: unconfiguredViewModel.context) + } + .previewDisplayName("Unsupported") + .snapshotPreferences(delay: 0.1) + } + + static func makeViewModel(homeserverAddress: String = "matrix.org", withCredentials: Bool = false) -> LoginScreenViewModel { + let authenticationService = AuthenticationService.mock + + Task { await authenticationService.configure(for: homeserverAddress, flow: .login) } + + let viewModel = LoginScreenViewModel(authenticationService: authenticationService, + slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL, + userIndicatorController: UserIndicatorControllerMock(), + analytics: ServiceLocator.shared.analytics) + + if withCredentials { + viewModel.context.username = "alice" + viewModel.context.password = "password" + } + + return viewModel } } diff --git a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenViewModel.swift b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenViewModel.swift index 127bf29ed2..b893a25328 100644 --- a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenViewModel.swift +++ b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/ServerConfirmationScreenViewModel.swift @@ -37,10 +37,8 @@ class ServerConfirmationScreenViewModel: ServerConfirmationScreenViewModelType, authenticationService.homeserver .receive(on: DispatchQueue.main) - .sink { [weak self] homeserver in - guard let self else { return } - state.homeserverAddress = homeserver.address - } + .map(\.address) + .weakAssign(to: \.state.homeserverAddress, on: self) .store(in: &cancellables) } diff --git a/ElementX/Sources/Services/Authentication/AuthenticationService.swift b/ElementX/Sources/Services/Authentication/AuthenticationService.swift index 64f5b91f78..092a097024 100644 --- a/ElementX/Sources/Services/Authentication/AuthenticationService.swift +++ b/ElementX/Sources/Services/Authentication/AuthenticationService.swift @@ -204,9 +204,11 @@ class AuthenticationService: AuthenticationServiceProtocol { // MARK: - Mocks extension AuthenticationService { - static var mock = AuthenticationService(userSessionStore: UserSessionStoreMock(configuration: .init()), - encryptionKeyProvider: EncryptionKeyProvider(), - clientBuilderFactory: AuthenticationClientBuilderFactoryMock(configuration: .init()), - appSettings: ServiceLocator.shared.settings, - appHooks: AppHooks()) + static var mock: AuthenticationService { + AuthenticationService(userSessionStore: UserSessionStoreMock(configuration: .init()), + encryptionKeyProvider: EncryptionKeyProvider(), + clientBuilderFactory: AuthenticationClientBuilderFactoryMock(configuration: .init()), + appSettings: ServiceLocator.shared.settings, + appHooks: AppHooks()) + } } diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.Credentials-Entered.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.Credentials-Entered.png index 9ba7a98cd0..dce2d64cd6 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.Credentials-Entered.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.Credentials-Entered.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79822b8f61589c6f261eb71f0ab15c6a6bd2d9f386499c8bc6a2db22628ecbcf -size 95885 +oid sha256:268e4db4b7fbbabd0804c70d5532c8632ee225d633304da1e136c489332ab306 +size 92683 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.OIDC-Fallback.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.OIDC-Fallback.png deleted file mode 100644 index 44e7172706..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.OIDC-Fallback.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3d59c0c3eee314426f46098f5c2341520eecfe59479cf1f6f79e967636b4b443 -size 99155 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.Unsupported.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.Unsupported.png index 44e7172706..93f5919749 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.Unsupported.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.Unsupported.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d59c0c3eee314426f46098f5c2341520eecfe59479cf1f6f79e967636b4b443 -size 99155 +oid sha256:d93585f86e776c0636eb9457cfa6acd35dfef6a8e9591b817aa9b41470ffb1cb +size 95029 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.matrix-org.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.matrix-org.png index 44e7172706..737046c6c0 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.matrix-org.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.matrix-org.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d59c0c3eee314426f46098f5c2341520eecfe59479cf1f6f79e967636b4b443 -size 99155 +oid sha256:9369cd8554cdfc4d1e53d73b33a890db9c7f9e8e39fe4075a58999ec5b2df8ca +size 95979 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.Credentials-Entered.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.Credentials-Entered.png index f3f2e13880..393349d96a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.Credentials-Entered.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.Credentials-Entered.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:253ca4671f65641fabd2ada1a7789821585e1c3e0146e098f96dc4a90280f1d6 -size 101236 +oid sha256:7821d479f4e7230da2df271d889e5c6147edadfbd2b9cadd391a7959605d74a4 +size 96911 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.OIDC-Fallback.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.OIDC-Fallback.png deleted file mode 100644 index 0084793ed8..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.OIDC-Fallback.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f40def8fbafb7262bb0fcbb806c75679ceb0c4912264e1dc912783bc5c627c21 -size 105228 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.Unsupported.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.Unsupported.png index 0084793ed8..f87f254ae6 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.Unsupported.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.Unsupported.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f40def8fbafb7262bb0fcbb806c75679ceb0c4912264e1dc912783bc5c627c21 -size 105228 +oid sha256:374a57ae7fcf402b65b10a2863b0a05128ac111b97b0ee88298d880f1fc8fb7c +size 116598 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.matrix-org.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.matrix-org.png index 0084793ed8..d3cc66a102 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.matrix-org.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.matrix-org.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f40def8fbafb7262bb0fcbb806c75679ceb0c4912264e1dc912783bc5c627c21 -size 105228 +oid sha256:415182fb2d280fdd1c6b723383621acad2275b1d2286de57e718e21c274e3823 +size 100924 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.Credentials-Entered.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.Credentials-Entered.png index 83d3932201..01556de3d9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.Credentials-Entered.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.Credentials-Entered.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e97384c64091c5b271c22a3487be59ed573659b0909464c535917cbf30cdf8c -size 51890 +oid sha256:f305e0b0e2423bd83e825f0a5fff222fc751ba9a9a397a48ec8884191fa649af +size 47648 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.OIDC-Fallback.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.OIDC-Fallback.png deleted file mode 100644 index 43857b08f7..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.OIDC-Fallback.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5c3efceb5056772c54a8f61e8c30b9a9e9c404b52ab089c57a651c54f52f82ba -size 54748 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.Unsupported.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.Unsupported.png index 43857b08f7..8787a8792a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.Unsupported.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.Unsupported.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c3efceb5056772c54a8f61e8c30b9a9e9c404b52ab089c57a651c54f52f82ba -size 54748 +oid sha256:c25500ebf350efd1e8fee87c2031b7caeb6e525e22a65db6d793301749a8474b +size 55931 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.matrix-org.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.matrix-org.png index 43857b08f7..b46de1923d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.matrix-org.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.matrix-org.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c3efceb5056772c54a8f61e8c30b9a9e9c404b52ab089c57a651c54f52f82ba -size 54748 +oid sha256:b57da444c1ba2db6cc6cc5f465753f56911079916a857d63435b79f1c64d6e00 +size 50637 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.Credentials-Entered.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.Credentials-Entered.png index 40f4a1bdc1..8bc5c3f3b6 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.Credentials-Entered.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.Credentials-Entered.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82da7f2c937aba87f3b0b6b8112e1bd57335c3292588f365f6824899baff129d -size 59471 +oid sha256:40434e10facf844daa52896c3cabb1ac9dfc05cfb1d25ea358ca1f0387012655 +size 55739 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.OIDC-Fallback.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.OIDC-Fallback.png deleted file mode 100644 index 727fafdfbc..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.OIDC-Fallback.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7e0f90b1d7bcc87d9cb94ee21b61133920e02191990d02442911a7135e1fd2a1 -size 63547 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.Unsupported.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.Unsupported.png index 727fafdfbc..faba525b6b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.Unsupported.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.Unsupported.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e0f90b1d7bcc87d9cb94ee21b61133920e02191990d02442911a7135e1fd2a1 -size 63547 +oid sha256:5eebc69dcd0285b9e83b7594f157f8aa45ebe217d2a186e981d821dd81847d46 +size 80515 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.matrix-org.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.matrix-org.png index 727fafdfbc..974eeb50e8 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.matrix-org.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.matrix-org.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e0f90b1d7bcc87d9cb94ee21b61133920e02191990d02442911a7135e1fd2a1 -size 63547 +oid sha256:76dbdda9f2d9b68d64a912d8d1b3e945449d71e6c5f557ae426d97e9b6b3a3ae +size 59817 diff --git a/UnitTests/Sources/LoginScreenViewModelTests.swift b/UnitTests/Sources/LoginScreenViewModelTests.swift new file mode 100644 index 0000000000..859b0ab064 --- /dev/null +++ b/UnitTests/Sources/LoginScreenViewModelTests.swift @@ -0,0 +1,173 @@ +// +// Copyright 2022-2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import XCTest + +@testable import ElementX + +@MainActor +class LoginScreenViewModelTests: XCTestCase { + var viewModel: LoginScreenViewModelProtocol! + var context: LoginScreenViewModelType.Context { viewModel.context } + + var clientBuilderFactory: AuthenticationClientBuilderFactoryMock! + var service: AuthenticationServiceProtocol! + + private func setupViewModel(homeserverAddress: String = "matrix.org") async { + clientBuilderFactory = AuthenticationClientBuilderFactoryMock(configuration: .init()) + service = AuthenticationService(userSessionStore: UserSessionStoreMock(configuration: .init()), + encryptionKeyProvider: EncryptionKeyProvider(), + clientBuilderFactory: clientBuilderFactory, + appSettings: ServiceLocator.shared.settings, + appHooks: AppHooks()) + + guard case .success = await service.configure(for: homeserverAddress, flow: .login) else { + XCTFail("A valid server should be configured for the test.") + return + } + + viewModel = LoginScreenViewModel(authenticationService: service, + slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL, + userIndicatorController: UserIndicatorControllerMock(), + analytics: ServiceLocator.shared.analytics) + } + + func testMatrixDotOrg() async { + // Given the initial view model configured for matrix.org. + await setupViewModel() + + // Then the view state should contain a homeserver that matches matrix.org and show the login form. + XCTAssertEqual(context.viewState.homeserver, .mockMatrixDotOrg, "The homeserver data should match the default homeserver.") + XCTAssertEqual(context.viewState.loginMode, .password, "The login form should be shown.") + } + + func testBasicServer() async { + // Given the view model configured for a basic server example.com that only supports password authentication. + await setupViewModel(homeserverAddress: "example.com") + + // Then the view state should be updated with the homeserver and show the login form. + XCTAssertEqual(context.viewState.homeserver, .mockBasicServer, "The homeserver data should should match the new homeserver.") + XCTAssertEqual(context.viewState.loginMode, .password, "The login form should be shown.") + } + + func testUsernameWithEmptyPassword() async { + // Given a form with an empty username and password. + await setupViewModel() + XCTAssertTrue(context.password.isEmpty, "The initial value for the password should be empty.") + XCTAssertTrue(context.username.isEmpty, "The initial value for the username should be empty.") + XCTAssertFalse(context.viewState.hasValidCredentials, "The credentials should be invalid.") + XCTAssertFalse(context.viewState.canSubmit, "The form should be blocked for submission.") + + // When entering a username without a password. + context.username = "bob" + context.password = "" + + // Then the credentials should be considered invalid. + XCTAssertFalse(context.viewState.hasValidCredentials, "The credentials should be invalid.") + XCTAssertFalse(context.viewState.canSubmit, "The form should be blocked for submission.") + } + + func testEmptyUsernameWithPassword() async { + // Given a form with an empty username and password. + await setupViewModel() + XCTAssertTrue(context.password.isEmpty, "The initial value for the password should be empty.") + XCTAssertTrue(context.username.isEmpty, "The initial value for the username should be empty.") + XCTAssertFalse(context.viewState.hasValidCredentials, "The credentials should be invalid.") + XCTAssertFalse(context.viewState.canSubmit, "The form should be blocked for submission.") + + // When entering a password without a username. + context.username = "" + context.password = "12345678" + + // Then the credentials should be considered invalid. + XCTAssertFalse(context.viewState.hasValidCredentials, "The credentials should be invalid.") + XCTAssertFalse(context.viewState.canSubmit, "The form should be blocked for submission.") + } + + func testValidCredentials() async { + // Given a form with an empty username and password. + await setupViewModel() + XCTAssertTrue(context.password.isEmpty, "The initial value for the password should be empty.") + XCTAssertTrue(context.username.isEmpty, "The initial value for the username should be empty.") + XCTAssertFalse(context.viewState.hasValidCredentials, "The credentials should be invalid.") + XCTAssertFalse(context.viewState.canSubmit, "The form should be blocked for submission.") + + // When entering a username and an 8-character password. + context.username = "bob" + context.password = "12345678" + + // Then the credentials should be considered valid. + XCTAssertTrue(context.viewState.hasValidCredentials, "The credentials should be valid when the username and password are valid.") + XCTAssertTrue(context.viewState.canSubmit, "The form should be ready to submit.") + } + + func testLoadingServerWithoutPassword() async throws { + // Given a form with valid credentials. + await setupViewModel() + context.username = "@bob:example.com" + XCTAssertFalse(context.viewState.hasValidCredentials, "The credentials should be not be valid without a password.") + XCTAssertFalse(context.viewState.isLoading, "The view shouldn't start in a loading state.") + XCTAssertFalse(context.viewState.canSubmit, "The form should not be submittable.") + + // When updating the view model whilst loading a homeserver. + let deferred = deferFulfillment(context.$viewState, keyPath: \.isLoading, transitionValues: [true, false]) + context.send(viewAction: .parseUsername) + + // Then the view state should represent the loading but never allow submitting to occur. + try await deferred.fulfill() + XCTAssertFalse(context.viewState.isLoading, "The view should be back in a loaded state.") + XCTAssertFalse(context.viewState.canSubmit, "The form should still not be submittable.") + } + + func testLoadingServerWithPasswordEntered() async throws { + // Given a form with valid credentials. + await setupViewModel() + context.username = "@bob:example.com" + context.password = "12345678" + XCTAssertTrue(context.viewState.hasValidCredentials, "The credentials should be valid.") + XCTAssertFalse(context.viewState.isLoading, "The view shouldn't start in a loading state.") + XCTAssertTrue(context.viewState.canSubmit, "The form should be ready to submit.") + + // When updating the view model whilst loading a homeserver. + let deferred = deferFulfillment(context.$viewState, keyPath: \.canSubmit, transitionValues: [false, true]) + context.send(viewAction: .parseUsername) + + // Then the view should be blocked from submitting while loading and then become unblocked again. + try await deferred.fulfill() + XCTAssertFalse(context.viewState.isLoading, "The view should be back in a loaded state.") + XCTAssertTrue(context.viewState.canSubmit, "The form should be ready to submit.") + } + + func testOIDCServer() async throws { + // Given the screen configured for matrix.org + await setupViewModel() + + // When entering a username for a user on a homeserver with OIDC. + let deferred = deferFulfillment(viewModel.actions) { $0.isConfiguredForOIDC } + context.username = "@bob:company.com" + context.send(viewAction: .parseUsername) + try await deferred.fulfill() + + // Then the view state should be updated with the homeserver and show the OIDC button. + XCTAssertTrue(context.viewState.loginMode.supportsOIDCFlow, "The OIDC button should be shown.") + } + + func testUnsupportedServer() async throws { + // Given the screen configured for matrix.org + await setupViewModel() + XCTAssertNil(context.alertInfo, "There shouldn't be an alert when the screen loads.") + + // When entering a username for an unsupported homeserver. + let deferred = deferFulfillment(context.$viewState) { $0.bindings.alertInfo != nil } + context.username = "@bob:server.net" + context.send(viewAction: .parseUsername) + try await deferred.fulfill() + + // Then the view state should be updated to show an alert. + XCTAssertEqual(context.alertInfo?.id, .unknown, "An alert should be shown to the user.") + } +} diff --git a/UnitTests/Sources/LoginViewModelTests.swift b/UnitTests/Sources/LoginViewModelTests.swift deleted file mode 100644 index ba02c88a92..0000000000 --- a/UnitTests/Sources/LoginViewModelTests.swift +++ /dev/null @@ -1,137 +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 XCTest - -@testable import ElementX - -@MainActor -class LoginViewModelTests: XCTestCase { - let defaultHomeserver = LoginHomeserver.mockMatrixDotOrg - var viewModel: LoginScreenViewModelProtocol! - var context: LoginScreenViewModelType.Context! - - @MainActor override func setUp() async throws { - viewModel = LoginScreenViewModel(homeserver: defaultHomeserver, slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL) - context = viewModel.context - } - - func testMatrixDotOrg() { - // Given the initial view model configured for matrix.org. - let homeserver = defaultHomeserver - - // Then the view state should contain a homeserver that matches matrix.org and show the login form. - XCTAssertEqual(context.viewState.homeserver, homeserver, "The homeserver data should match the original.") - XCTAssertEqual(context.viewState.loginMode, .password, "The login form should be shown.") - } - - func testBasicServer() { - // Given a basic server example.com that only supports password registration. - let homeserver = LoginHomeserver.mockBasicServer - - // When updating the view model with the server. - viewModel.update(homeserver: homeserver) - - // Then the view state should be updated with the homeserver and show the login form. - XCTAssertEqual(context.viewState.homeserver, homeserver, "The homeserver data should should match the new homeserver.") - XCTAssertEqual(context.viewState.loginMode, .password, "The login form should be shown.") - } - - func testUsernameWithEmptyPassword() { - // Given a form with an empty username and password. - XCTAssertTrue(context.password.isEmpty, "The initial value for the password should be empty.") - XCTAssertTrue(context.username.isEmpty, "The initial value for the username should be empty.") - XCTAssertFalse(context.viewState.hasValidCredentials, "The credentials should be invalid.") - XCTAssertFalse(context.viewState.canSubmit, "The form should be blocked for submission.") - - // When entering a username without a password. - context.username = "bob" - context.password = "" - - // Then the credentials should be considered invalid. - XCTAssertFalse(context.viewState.hasValidCredentials, "The credentials should be invalid.") - XCTAssertFalse(context.viewState.canSubmit, "The form should be blocked for submission.") - } - - func testEmptyUsernameWithPassword() { - // Given a form with an empty username and password. - XCTAssertTrue(context.password.isEmpty, "The initial value for the password should be empty.") - XCTAssertTrue(context.username.isEmpty, "The initial value for the username should be empty.") - XCTAssertFalse(context.viewState.hasValidCredentials, "The credentials should be invalid.") - XCTAssertFalse(context.viewState.canSubmit, "The form should be blocked for submission.") - - // When entering a password without a username. - context.username = "" - context.password = "12345678" - - // Then the credentials should be considered invalid. - XCTAssertFalse(context.viewState.hasValidCredentials, "The credentials should be invalid.") - XCTAssertFalse(context.viewState.canSubmit, "The form should be blocked for submission.") - } - - func testValidCredentials() { - // Given a form with an empty username and password. - XCTAssertTrue(context.password.isEmpty, "The initial value for the password should be empty.") - XCTAssertTrue(context.username.isEmpty, "The initial value for the username should be empty.") - XCTAssertFalse(context.viewState.hasValidCredentials, "The credentials should be invalid.") - XCTAssertFalse(context.viewState.canSubmit, "The form should be blocked for submission.") - - // When entering a username and an 8-character password. - context.username = "bob" - context.password = "12345678" - - // Then the credentials should be considered valid. - XCTAssertTrue(context.viewState.hasValidCredentials, "The credentials should be valid when the username and password are valid.") - XCTAssertTrue(context.viewState.canSubmit, "The form should be ready to submit.") - } - - func testLoadingServer() { - // Given a form with valid credentials. - context.username = "bob" - context.password = "12345678" - XCTAssertTrue(context.viewState.hasValidCredentials, "The credentials should be valid.") - XCTAssertFalse(context.viewState.isLoading, "The view shouldn't start in a loading state.") - XCTAssertTrue(context.viewState.canSubmit, "The form should be ready to submit.") - - // When updating the view model whilst loading a homeserver. - viewModel.update(isLoading: true) - - // Then the view state should reflect that the homeserver is loading. - XCTAssertTrue(context.viewState.isLoading, "The view should now be in a loading state.") - XCTAssertFalse(context.viewState.canSubmit, "The form should be blocked for submission.") - - // When updating the view model after loading a homeserver. - viewModel.update(isLoading: false) - - // Then the view state should reflect that the homeserver is now loaded. - XCTAssertFalse(context.viewState.isLoading, "The view should be back in a loaded state.") - XCTAssertTrue(context.viewState.canSubmit, "The form should be ready to submit.") - } - - func testOIDCServer() { - // Given a basic server example.com that supports OIDC registration. - let homeserver = LoginHomeserver.mockOIDC - - // When updating the view model with the server. - viewModel.update(homeserver: homeserver) - - // Then the view state should be updated with the homeserver and show the OIDC button. - XCTAssertTrue(context.viewState.loginMode.supportsOIDCFlow, "The OIDC button should be shown.") - } - - func testLogsForPassword() { - // Given the coordinator and view model results that contain passwords. - let password = "supersecretpassword" - let viewModelAction: LoginScreenViewModelAction = .login(username: "Alice", password: password) - - // When creating a string representation of those results (e.g. for logging). - let viewModelActionString = "\(viewModelAction)" - - // Then the password should not be included in that string. - XCTAssertFalse("\(viewModelActionString)".contains(password), "The password must not be included in any strings.") - } -} diff --git a/UnitTests/Sources/ServerSelectionViewModelTests.swift b/UnitTests/Sources/ServerSelectionScreenViewModelTests.swift similarity index 99% rename from UnitTests/Sources/ServerSelectionViewModelTests.swift rename to UnitTests/Sources/ServerSelectionScreenViewModelTests.swift index fc6411102c..a4fd808691 100644 --- a/UnitTests/Sources/ServerSelectionViewModelTests.swift +++ b/UnitTests/Sources/ServerSelectionScreenViewModelTests.swift @@ -10,7 +10,7 @@ import XCTest @testable import ElementX @MainActor -class ServerSelectionViewModelTests: XCTestCase { +class ServerSelectionScreenViewModelTests: XCTestCase { var clientBuilderFactory: AuthenticationClientBuilderFactoryMock! var service: AuthenticationServiceProtocol! From c79dbe85a0925f87361ba2d6da1140b0b7a13485 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Tue, 1 Oct 2024 18:50:11 +0300 Subject: [PATCH 024/114] Bump the RustSDK to v1.0.53: adopt latest record based timeline item APIs (#3356) --- ElementX.xcodeproj/project.pbxproj | 14 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- .../Application/AppMediatorProtocol.swift | 4 +- .../Sources/Mocks/EventTimelineItem.swift | 56 + .../Mocks/Generated/SDKGeneratedMocks.swift | 2497 ++++------------- ElementX/Sources/Mocks/PollMock.swift | 2 +- .../Mocks/RoomTimelineProviderMock.swift | 7 +- .../Mocks/SDK/EventTimelineItemSDKMock.swift | 29 - ElementX/Sources/Mocks/TimelineItemMock.swift | 66 - ElementX/Sources/Other/Extensions/Array.swift | 2 +- .../Other/Extensions/ProposedViewSize.swift | 2 +- ElementX/Sources/Other/Extensions/URL.swift | 2 +- .../View/MessageForwardingScreen.swift | 2 +- .../ComposerToolbarViewModel.swift | 6 +- .../View/ComposerToolbar.swift | 4 +- .../Screens/Timeline/TimelineModels.swift | 2 +- .../TimelineTableViewController.swift | 14 +- .../Screens/Timeline/TimelineViewModel.swift | 10 +- .../View/Style/SwipeToReplyView.swift | 2 +- .../Style/TimelineItemBubbledStylerView.swift | 38 +- .../Timeline/View/Style/TimelineStyler.swift | 8 +- .../TimelineItemStatusView.swift | 2 +- .../Supplementary/TimelineReactionsView.swift | 8 +- .../CollapsibleRoomTimelineView.swift | 4 +- .../ReadMarkerRoomTimelineView.swift | 10 +- .../SeparatorRoomTimelineView.swift | 2 +- .../AuthenticationServiceProtocol.swift | 2 +- .../RoomSummary/RoomEventStringBuilder.swift | 11 +- .../RoomSummary/RoomSummaryProvider.swift | 2 +- .../SecureBackup/SecureBackupController.swift | 2 +- .../Fixtures/RoomTimelineItemFixtures.swift | 34 +- .../Timeline/RoomTimelineProvider.swift | 35 +- .../RoomTimelineController.swift | 10 +- .../CustomStringConvertible.swift | 14 +- .../Services/Timeline/TimelineItemProxy.swift | 70 +- .../PaginationIndicatorRoomTimelineItem.swift | 2 +- .../TimelineStartRoomTimelineItem.swift | 2 +- .../RoomTimelineItemFactory.swift | 212 +- .../RoomTimelineItemViewState.swift | 24 +- .../Services/Timeline/TimelineProxy.swift | 37 +- UITests/Sources/PollFormScreenUITests.swift | 6 +- UITests/Sources/UserSessionScreenTests.swift | 2 +- .../ComposerToolbarViewModelTests.swift | 41 +- UnitTests/Sources/LoggingTests.swift | 14 +- ...essageForwardingScreenViewModelTests.swift | 2 +- .../Sources/RoomScreenViewModelTests.swift | 17 +- .../Sources/TimelineItemFactoryTests.swift | 16 +- .../Sources/TimelineViewModelTests.swift | 14 +- project.yml | 2 +- 49 files changed, 986 insertions(+), 2382 deletions(-) create mode 100644 ElementX/Sources/Mocks/EventTimelineItem.swift delete mode 100644 ElementX/Sources/Mocks/SDK/EventTimelineItemSDKMock.swift delete mode 100644 ElementX/Sources/Mocks/TimelineItemMock.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index c4c8ffe5a9..29406d27a5 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -137,7 +137,6 @@ 1B8E30B35BF8F541C1318F19 /* SecureBackupScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40316EFFEAC7B206EE9A55AE /* SecureBackupScreenViewModelTests.swift */; }; 1BA04D05EBC6646958B1BE60 /* PlaceholderScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF34A2FD6797535C95AC918D /* PlaceholderScreenCoordinator.swift */; }; 1C409A26A99F0371C47AFA51 /* UserDiscoveryServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F615A00DB223FF3280204D2 /* UserDiscoveryServiceProtocol.swift */; }; - 1C815DD79B401DEBA2914773 /* TimelineItemMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A1003A0F7A1DFB47F4E2D0 /* TimelineItemMock.swift */; }; 1C8BC70A18060677E295A846 /* ShareToMapsAppActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4481799F455B3DA243BDA2AC /* ShareToMapsAppActivity.swift */; }; 1C9BB74711E5F24C77B7FED0 /* RoomMembersListScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AEA0B743847CFA5B3C38EE4 /* RoomMembersListScreenCoordinator.swift */; }; 1D5DC685CED904386C89B7DA /* NSRegularExpresion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95BAC0F6C9644336E9567EE6 /* NSRegularExpresion.swift */; }; @@ -211,6 +210,7 @@ 2DD9D0FE7CB5CFC80D071451 /* AppLockScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3E67E09FE5A35D73818C39 /* AppLockScreenModels.swift */; }; 2E43A3D221BE9587BC19C3F1 /* MatrixEntityRegexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */; }; 2E8C6672D0EE7D5B1BEDB8E2 /* ServerConfirmationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7478623CECC9438014244BA /* ServerConfirmationScreen.swift */; }; + 2F09DF0CB213CAE86A3E3B67 /* EventTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B10423B9102086A2D9BFCBA /* EventTimelineItem.swift */; }; 2F1CF90A3460C153154427F0 /* RoomScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086B997409328F091EBA43CE /* RoomScreenUITests.swift */; }; 2F6207CB5C4715FE313B1E95 /* TimelineViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6509708F54FC883604DFDC95 /* TimelineViewModelTests.swift */; }; 2F623DA1122140A987B34D08 /* NotificationSettingsEditScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA7BB497B2F539C17E88F6B7 /* NotificationSettingsEditScreenViewModelProtocol.swift */; }; @@ -407,7 +407,6 @@ 5AE6404C4FD4848ACCFF9EDC /* SecureBackupLogoutConfirmationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1573D28C8A9FB6399D0EEFB /* SecureBackupLogoutConfirmationScreenCoordinator.swift */; }; 5B6E5AD224509E6C0B520D6E /* RoomMemberDetailsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DDF49CEBC0DFC59C308335F /* RoomMemberDetailsScreenViewModelProtocol.swift */; }; 5B7D24A318AFF75AD611A026 /* RoomDirectorySearchScreenScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6BFF453838CF6C3982C5A3 /* RoomDirectorySearchScreenScreenViewModelTests.swift */; }; - 5BBDF9926CB645DE2F7BC258 /* EventTimelineItemSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86D447D771CEF6194348F5F /* EventTimelineItemSDKMock.swift */; }; 5BC6C4ADFE7F2A795ECDE130 /* SecureBackupKeyBackupScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2D4EEBE8C098BBADD10939 /* SecureBackupKeyBackupScreenCoordinator.swift */; }; 5C02841B2A86327B2C377682 /* NotificationConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C830A64609CBD152F06E0457 /* NotificationConstants.swift */; }; 5C164551F7D26E24F09083D3 /* StaticLocationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C616D90B1E2F033CAA325439 /* StaticLocationScreenViewModelProtocol.swift */; }; @@ -1309,6 +1308,7 @@ 1A4D29F2683F5772AC72406F /* MapTilerStaticMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStaticMap.swift; sourceTree = ""; }; 1A7ED2EF5BDBAD2A7DBC4636 /* GeoURITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeoURITests.swift; sourceTree = ""; }; 1B065EC39C99C1303A101C1C /* WebRegistrationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRegistrationScreen.swift; sourceTree = ""; }; + 1B10423B9102086A2D9BFCBA /* EventTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTimelineItem.swift; sourceTree = ""; }; 1B2AC540DE619B36832A5DB5 /* LocationRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationRoomTimelineItem.swift; sourceTree = ""; }; 1B53D6C5C0D14B04D3AB3F6E /* PillAttachmentViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillAttachmentViewProvider.swift; sourceTree = ""; }; 1B564D748B67A156F413CD97 /* NotificationSettingsEditScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreenModels.swift; sourceTree = ""; }; @@ -1904,7 +1904,6 @@ A433BE28B40D418237BE37B5 /* ReportContentScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentScreen.swift; sourceTree = ""; }; A436057DBEA1A23CA8CB1FD7 /* UIFont+AttributedStringBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIFont+AttributedStringBuilder.h"; sourceTree = ""; }; A443FAE2EE820A5790C35C8D /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et; path = et.lproj/Localizable.strings; sourceTree = ""; }; - A4A1003A0F7A1DFB47F4E2D0 /* TimelineItemMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemMock.swift; sourceTree = ""; }; A54AAF72E821B4084B7E4298 /* PinnedEventsTimelineFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedEventsTimelineFlowCoordinator.swift; sourceTree = ""; }; A58E93D91DE3288010390DEE /* EmojiDetectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiDetectionTests.swift; sourceTree = ""; }; A6702BC84D3CC2421D78CD4E /* WebRegistrationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRegistrationScreenViewModel.swift; sourceTree = ""; }; @@ -2003,7 +2002,6 @@ B81B6170DB690013CEB646F4 /* MapLibreModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapLibreModels.swift; sourceTree = ""; }; B8516302ACCA94A0E680AB3B /* VoiceMessageButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageButton.swift; sourceTree = ""; }; B858A61F2A570DFB8DE570A7 /* AggregratedReaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AggregratedReaction.swift; sourceTree = ""; }; - B86D447D771CEF6194348F5F /* EventTimelineItemSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTimelineItemSDKMock.swift; sourceTree = ""; }; B8A3B7637DDBD6AA97AC2545 /* CameraPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPicker.swift; sourceTree = ""; }; B8F28602AC7AC881AED37EBA /* NavigationCoordinators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationCoordinators.swift; sourceTree = ""; }; B902EA6CD3296B0E10EE432B /* HomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreen.swift; sourceTree = ""; }; @@ -2899,6 +2897,7 @@ E2F96CCBEAAA7F2185BFA354 /* ClientProxyMock.swift */, 4E600B315B920B9687F8EE1B /* ComposerDraftServiceMock.swift */, E321E840DCC63790049984F4 /* ElementCallServiceMock.swift */, + 1B10423B9102086A2D9BFCBA /* EventTimelineItem.swift */, 867DC9530C42F7B5176BE465 /* JoinedRoomProxyMock.swift */, 8DA1E8F287680C8ED25EDBAC /* NetworkMonitorMock.swift */, 382B50F7E379B3DBBD174364 /* NotificationSettingsProxyMock.swift */, @@ -2911,7 +2910,6 @@ D479DF730528153665E5782E /* RoomTimelineControllerFactoryMock.swift */, F74532E01B317C56C1BE8FA8 /* RoomTimelineProviderMock.swift */, 248649EBA5BC33DB93698734 /* SessionVerificationControllerProxyMock.swift */, - A4A1003A0F7A1DFB47F4E2D0 /* TimelineItemMock.swift */, 7893780A1FD6E3F38B3E9049 /* UserIndicatorControllerMock.swift */, AAD01F7FC2BBAC7351948595 /* UserProfile+Mock.swift */, F4469F6AE311BDC439B3A5EC /* UserSessionMock.swift */, @@ -5257,7 +5255,6 @@ isa = PBXGroup; children = ( 8EAF4A49F3ACD8BB8B0D2371 /* ClientSDKMock.swift */, - B86D447D771CEF6194348F5F /* EventTimelineItemSDKMock.swift */, ); path = SDK; sourceTree = ""; @@ -6449,7 +6446,7 @@ 50539366B408780B232C1910 /* EstimatedWaveformView.swift in Sources */, F78BAD28482A467287A9A5A3 /* EventBasedMessageTimelineItemProtocol.swift in Sources */, 02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */, - 5BBDF9926CB645DE2F7BC258 /* EventTimelineItemSDKMock.swift in Sources */, + 2F09DF0CB213CAE86A3E3B67 /* EventTimelineItem.swift in Sources */, 63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */, 5F06AD3C66884CE793AE6119 /* FileManager.swift in Sources */, D33AC79A50DFC26D2498DD28 /* FileRoomTimelineItem.swift in Sources */, @@ -6933,7 +6930,6 @@ 6C98153D60FF9B648C166C27 /* TimelineItemMenu.swift in Sources */, AE07F215EBC2B9CBF17AA54B /* TimelineItemMenuAction.swift in Sources */, 12CD8B5CC30A05061228BF9E /* TimelineItemMenuActionProvider.swift in Sources */, - 1C815DD79B401DEBA2914773 /* TimelineItemMock.swift in Sources */, 440123E29E2F9B001A775BBE /* TimelineItemProxy.swift in Sources */, 9586E90A447C4896C0CA3A8E /* TimelineItemReplyDetails.swift in Sources */, F3ECA377FF77E81A4F1FA062 /* TimelineItemSendInfoLabel.swift in Sources */, @@ -7799,7 +7795,7 @@ repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.0.52; + version = 1.0.53; }; }; 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index dfbedefd5d..eba7ed56ab 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/matrix-rust-components-swift", "state" : { - "revision" : "11c6d99d62035b02f4c448daebff4d63c962da20", - "version" : "1.0.52" + "revision" : "83abbdc8485340c20f27148153ff62f690ca210b", + "version" : "1.0.53" } }, { diff --git a/ElementX/Sources/Application/AppMediatorProtocol.swift b/ElementX/Sources/Application/AppMediatorProtocol.swift index d7043f2983..090dffba59 100644 --- a/ElementX/Sources/Application/AppMediatorProtocol.swift +++ b/ElementX/Sources/Application/AppMediatorProtocol.swift @@ -29,7 +29,7 @@ protocol AppMediatorProtocol { func requestAuthorizationIfNeeded() async -> Bool } -extension UIApplication.State: CustomStringConvertible { +extension UIApplication.State: @retroactive CustomStringConvertible { public var description: String { switch self { case .active: @@ -44,7 +44,7 @@ extension UIApplication.State: CustomStringConvertible { } } -extension UIUserInterfaceActiveAppearance: CustomStringConvertible { +extension UIUserInterfaceActiveAppearance: @retroactive CustomStringConvertible { public var description: String { switch self { case .active: diff --git a/ElementX/Sources/Mocks/EventTimelineItem.swift b/ElementX/Sources/Mocks/EventTimelineItem.swift new file mode 100644 index 0000000000..010e040a5c --- /dev/null +++ b/ElementX/Sources/Mocks/EventTimelineItem.swift @@ -0,0 +1,56 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Foundation +import LoremSwiftum +import MatrixRustSDK + +struct EventTimelineItemSDKMockConfiguration { + var eventID: String = UUID().uuidString + var sender = "" + var isOwn = false + var content: TimelineItemContent = .redactedMessage +} + +extension EventTimelineItem { + init(configuration: EventTimelineItemSDKMockConfiguration) { + self.init(isLocal: false, + isRemote: true, + eventOrTransactionId: .eventId(eventId: configuration.eventID), + sender: configuration.sender, + senderProfile: .pending, + isOwn: configuration.isOwn, + isEditable: false, + content: configuration.content, + timestamp: 0, + reactions: [], + debugInfoProvider: EventTimelineItemDebugInfoProviderSDKMock(), + localSendState: nil, + readReceipts: [:], + origin: nil, + canBeRepliedTo: false, + shieldsProvider: EventShieldsProviderSDKMock()) + } + + static var mockMessage: EventTimelineItem { + let body = Lorem.sentences(Int.random(in: 1...5)) + let messageType = MessageType.text(content: .init(body: body, formatted: nil)) + + let content = TimelineItemContent.message(content: .init(msgType: messageType, + body: body, + inReplyTo: nil, + threadRoot: nil, + isEdited: false, + mentions: nil)) + + return .init(configuration: .init(content: content)) + } + + static func mockCallInvite(sender: String) -> EventTimelineItem { + .init(configuration: .init(sender: sender, content: .callInvite)) + } +} diff --git a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift index 152c0940c9..4cd45da022 100644 --- a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift @@ -5949,16 +5949,16 @@ open class EncryptionSDKMock: MatrixRustSDK.Encryption { //MARK: - enableRecovery - open var enableRecoveryWaitForBackupsToUploadProgressListenerThrowableError: Error? - var enableRecoveryWaitForBackupsToUploadProgressListenerUnderlyingCallsCount = 0 - open var enableRecoveryWaitForBackupsToUploadProgressListenerCallsCount: Int { + open var enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerThrowableError: Error? + var enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerUnderlyingCallsCount = 0 + open var enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerCallsCount: Int { get { if Thread.isMainThread { - return enableRecoveryWaitForBackupsToUploadProgressListenerUnderlyingCallsCount + return enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = enableRecoveryWaitForBackupsToUploadProgressListenerUnderlyingCallsCount + returnValue = enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerUnderlyingCallsCount } return returnValue! @@ -5966,29 +5966,29 @@ open class EncryptionSDKMock: MatrixRustSDK.Encryption { } set { if Thread.isMainThread { - enableRecoveryWaitForBackupsToUploadProgressListenerUnderlyingCallsCount = newValue + enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - enableRecoveryWaitForBackupsToUploadProgressListenerUnderlyingCallsCount = newValue + enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerUnderlyingCallsCount = newValue } } } } - open var enableRecoveryWaitForBackupsToUploadProgressListenerCalled: Bool { - return enableRecoveryWaitForBackupsToUploadProgressListenerCallsCount > 0 + open var enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerCalled: Bool { + return enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerCallsCount > 0 } - open var enableRecoveryWaitForBackupsToUploadProgressListenerReceivedArguments: (waitForBackupsToUpload: Bool, progressListener: EnableRecoveryProgressListener)? - open var enableRecoveryWaitForBackupsToUploadProgressListenerReceivedInvocations: [(waitForBackupsToUpload: Bool, progressListener: EnableRecoveryProgressListener)] = [] + open var enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerReceivedArguments: (waitForBackupsToUpload: Bool, passphrase: String?, progressListener: EnableRecoveryProgressListener)? + open var enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerReceivedInvocations: [(waitForBackupsToUpload: Bool, passphrase: String?, progressListener: EnableRecoveryProgressListener)] = [] - var enableRecoveryWaitForBackupsToUploadProgressListenerUnderlyingReturnValue: String! - open var enableRecoveryWaitForBackupsToUploadProgressListenerReturnValue: String! { + var enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerUnderlyingReturnValue: String! + open var enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerReturnValue: String! { get { if Thread.isMainThread { - return enableRecoveryWaitForBackupsToUploadProgressListenerUnderlyingReturnValue + return enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerUnderlyingReturnValue } else { var returnValue: String? = nil DispatchQueue.main.sync { - returnValue = enableRecoveryWaitForBackupsToUploadProgressListenerUnderlyingReturnValue + returnValue = enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerUnderlyingReturnValue } return returnValue! @@ -5996,29 +5996,29 @@ open class EncryptionSDKMock: MatrixRustSDK.Encryption { } set { if Thread.isMainThread { - enableRecoveryWaitForBackupsToUploadProgressListenerUnderlyingReturnValue = newValue + enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - enableRecoveryWaitForBackupsToUploadProgressListenerUnderlyingReturnValue = newValue + enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerUnderlyingReturnValue = newValue } } } } - open var enableRecoveryWaitForBackupsToUploadProgressListenerClosure: ((Bool, EnableRecoveryProgressListener) async throws -> String)? + open var enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerClosure: ((Bool, String?, EnableRecoveryProgressListener) async throws -> String)? - open override func enableRecovery(waitForBackupsToUpload: Bool, progressListener: EnableRecoveryProgressListener) async throws -> String { - if let error = enableRecoveryWaitForBackupsToUploadProgressListenerThrowableError { + open override func enableRecovery(waitForBackupsToUpload: Bool, passphrase: String?, progressListener: EnableRecoveryProgressListener) async throws -> String { + if let error = enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerThrowableError { throw error } - enableRecoveryWaitForBackupsToUploadProgressListenerCallsCount += 1 - enableRecoveryWaitForBackupsToUploadProgressListenerReceivedArguments = (waitForBackupsToUpload: waitForBackupsToUpload, progressListener: progressListener) + enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerCallsCount += 1 + enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerReceivedArguments = (waitForBackupsToUpload: waitForBackupsToUpload, passphrase: passphrase, progressListener: progressListener) DispatchQueue.main.async { - self.enableRecoveryWaitForBackupsToUploadProgressListenerReceivedInvocations.append((waitForBackupsToUpload: waitForBackupsToUpload, progressListener: progressListener)) + self.enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerReceivedInvocations.append((waitForBackupsToUpload: waitForBackupsToUpload, passphrase: passphrase, progressListener: progressListener)) } - if let enableRecoveryWaitForBackupsToUploadProgressListenerClosure = enableRecoveryWaitForBackupsToUploadProgressListenerClosure { - return try await enableRecoveryWaitForBackupsToUploadProgressListenerClosure(waitForBackupsToUpload, progressListener) + if let enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerClosure = enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerClosure { + return try await enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerClosure(waitForBackupsToUpload, passphrase, progressListener) } else { - return enableRecoveryWaitForBackupsToUploadProgressListenerReturnValue + return enableRecoveryWaitForBackupsToUploadPassphraseProgressListenerReturnValue } } @@ -6704,7 +6704,7 @@ open class EncryptionSDKMock: MatrixRustSDK.Encryption { await waitForE2eeInitializationTasksClosure?() } } -open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { +open class EventShieldsProviderSDKMock: MatrixRustSDK.EventShieldsProvider { init() { super.init(noPointer: .init()) } @@ -6715,17 +6715,17 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { fileprivate var pointer: UnsafeMutableRawPointer! - //MARK: - canBeRepliedTo + //MARK: - getShields - var canBeRepliedToUnderlyingCallsCount = 0 - open var canBeRepliedToCallsCount: Int { + var getShieldsStrictUnderlyingCallsCount = 0 + open var getShieldsStrictCallsCount: Int { get { if Thread.isMainThread { - return canBeRepliedToUnderlyingCallsCount + return getShieldsStrictUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = canBeRepliedToUnderlyingCallsCount + returnValue = getShieldsStrictUnderlyingCallsCount } return returnValue! @@ -6733,27 +6733,29 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - canBeRepliedToUnderlyingCallsCount = newValue + getShieldsStrictUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - canBeRepliedToUnderlyingCallsCount = newValue + getShieldsStrictUnderlyingCallsCount = newValue } } } } - open var canBeRepliedToCalled: Bool { - return canBeRepliedToCallsCount > 0 + open var getShieldsStrictCalled: Bool { + return getShieldsStrictCallsCount > 0 } + open var getShieldsStrictReceivedStrict: Bool? + open var getShieldsStrictReceivedInvocations: [Bool] = [] - var canBeRepliedToUnderlyingReturnValue: Bool! - open var canBeRepliedToReturnValue: Bool! { + var getShieldsStrictUnderlyingReturnValue: ShieldState? + open var getShieldsStrictReturnValue: ShieldState? { get { if Thread.isMainThread { - return canBeRepliedToUnderlyingReturnValue + return getShieldsStrictUnderlyingReturnValue } else { - var returnValue: Bool? = nil + var returnValue: ShieldState?? = nil DispatchQueue.main.sync { - returnValue = canBeRepliedToUnderlyingReturnValue + returnValue = getShieldsStrictUnderlyingReturnValue } return returnValue! @@ -6761,36 +6763,51 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - canBeRepliedToUnderlyingReturnValue = newValue + getShieldsStrictUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - canBeRepliedToUnderlyingReturnValue = newValue + getShieldsStrictUnderlyingReturnValue = newValue } } } } - open var canBeRepliedToClosure: (() -> Bool)? + open var getShieldsStrictClosure: ((Bool) -> ShieldState?)? - open override func canBeRepliedTo() -> Bool { - canBeRepliedToCallsCount += 1 - if let canBeRepliedToClosure = canBeRepliedToClosure { - return canBeRepliedToClosure() + open override func getShields(strict: Bool) -> ShieldState? { + getShieldsStrictCallsCount += 1 + getShieldsStrictReceivedStrict = strict + DispatchQueue.main.async { + self.getShieldsStrictReceivedInvocations.append(strict) + } + if let getShieldsStrictClosure = getShieldsStrictClosure { + return getShieldsStrictClosure(strict) } else { - return canBeRepliedToReturnValue + return getShieldsStrictReturnValue } } +} +open class EventTimelineItemDebugInfoProviderSDKMock: MatrixRustSDK.EventTimelineItemDebugInfoProvider { + init() { + super.init(noPointer: .init()) + } - //MARK: - content + public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + fatalError("init(unsafeFromRawPointer:) has not been implemented") + } + + fileprivate var pointer: UnsafeMutableRawPointer! + + //MARK: - get - var contentUnderlyingCallsCount = 0 - open var contentCallsCount: Int { + var getUnderlyingCallsCount = 0 + open var getCallsCount: Int { get { if Thread.isMainThread { - return contentUnderlyingCallsCount + return getUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = contentUnderlyingCallsCount + returnValue = getUnderlyingCallsCount } return returnValue! @@ -6798,27 +6815,27 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - contentUnderlyingCallsCount = newValue + getUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - contentUnderlyingCallsCount = newValue + getUnderlyingCallsCount = newValue } } } } - open var contentCalled: Bool { - return contentCallsCount > 0 + open var getCalled: Bool { + return getCallsCount > 0 } - var contentUnderlyingReturnValue: TimelineItemContent! - open var contentReturnValue: TimelineItemContent! { + var getUnderlyingReturnValue: EventTimelineItemDebugInfo! + open var getReturnValue: EventTimelineItemDebugInfo! { get { if Thread.isMainThread { - return contentUnderlyingReturnValue + return getUnderlyingReturnValue } else { - var returnValue: TimelineItemContent? = nil + var returnValue: EventTimelineItemDebugInfo? = nil DispatchQueue.main.sync { - returnValue = contentUnderlyingReturnValue + returnValue = getUnderlyingReturnValue } return returnValue! @@ -6826,36 +6843,47 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - contentUnderlyingReturnValue = newValue + getUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - contentUnderlyingReturnValue = newValue + getUnderlyingReturnValue = newValue } } } } - open var contentClosure: (() -> TimelineItemContent)? + open var getClosure: (() -> EventTimelineItemDebugInfo)? - open override func content() -> TimelineItemContent { - contentCallsCount += 1 - if let contentClosure = contentClosure { - return contentClosure() + open override func get() -> EventTimelineItemDebugInfo { + getCallsCount += 1 + if let getClosure = getClosure { + return getClosure() } else { - return contentReturnValue + return getReturnValue } } +} +open class HomeserverLoginDetailsSDKMock: MatrixRustSDK.HomeserverLoginDetails { + init() { + super.init(noPointer: .init()) + } + + public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + fatalError("init(unsafeFromRawPointer:) has not been implemented") + } - //MARK: - debugInfo + fileprivate var pointer: UnsafeMutableRawPointer! + + //MARK: - slidingSyncVersion - var debugInfoUnderlyingCallsCount = 0 - open var debugInfoCallsCount: Int { + var slidingSyncVersionUnderlyingCallsCount = 0 + open var slidingSyncVersionCallsCount: Int { get { if Thread.isMainThread { - return debugInfoUnderlyingCallsCount + return slidingSyncVersionUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = debugInfoUnderlyingCallsCount + returnValue = slidingSyncVersionUnderlyingCallsCount } return returnValue! @@ -6863,27 +6891,27 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - debugInfoUnderlyingCallsCount = newValue + slidingSyncVersionUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - debugInfoUnderlyingCallsCount = newValue + slidingSyncVersionUnderlyingCallsCount = newValue } } } } - open var debugInfoCalled: Bool { - return debugInfoCallsCount > 0 + open var slidingSyncVersionCalled: Bool { + return slidingSyncVersionCallsCount > 0 } - var debugInfoUnderlyingReturnValue: EventTimelineItemDebugInfo! - open var debugInfoReturnValue: EventTimelineItemDebugInfo! { + var slidingSyncVersionUnderlyingReturnValue: SlidingSyncVersion! + open var slidingSyncVersionReturnValue: SlidingSyncVersion! { get { if Thread.isMainThread { - return debugInfoUnderlyingReturnValue + return slidingSyncVersionUnderlyingReturnValue } else { - var returnValue: EventTimelineItemDebugInfo? = nil + var returnValue: SlidingSyncVersion? = nil DispatchQueue.main.sync { - returnValue = debugInfoUnderlyingReturnValue + returnValue = slidingSyncVersionUnderlyingReturnValue } return returnValue! @@ -6891,36 +6919,36 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - debugInfoUnderlyingReturnValue = newValue + slidingSyncVersionUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - debugInfoUnderlyingReturnValue = newValue + slidingSyncVersionUnderlyingReturnValue = newValue } } } } - open var debugInfoClosure: (() -> EventTimelineItemDebugInfo)? + open var slidingSyncVersionClosure: (() -> SlidingSyncVersion)? - open override func debugInfo() -> EventTimelineItemDebugInfo { - debugInfoCallsCount += 1 - if let debugInfoClosure = debugInfoClosure { - return debugInfoClosure() + open override func slidingSyncVersion() -> SlidingSyncVersion { + slidingSyncVersionCallsCount += 1 + if let slidingSyncVersionClosure = slidingSyncVersionClosure { + return slidingSyncVersionClosure() } else { - return debugInfoReturnValue + return slidingSyncVersionReturnValue } } - //MARK: - eventId + //MARK: - supportsOidcLogin - var eventIdUnderlyingCallsCount = 0 - open var eventIdCallsCount: Int { + var supportsOidcLoginUnderlyingCallsCount = 0 + open var supportsOidcLoginCallsCount: Int { get { if Thread.isMainThread { - return eventIdUnderlyingCallsCount + return supportsOidcLoginUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = eventIdUnderlyingCallsCount + returnValue = supportsOidcLoginUnderlyingCallsCount } return returnValue! @@ -6928,27 +6956,27 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - eventIdUnderlyingCallsCount = newValue + supportsOidcLoginUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - eventIdUnderlyingCallsCount = newValue + supportsOidcLoginUnderlyingCallsCount = newValue } } } } - open var eventIdCalled: Bool { - return eventIdCallsCount > 0 + open var supportsOidcLoginCalled: Bool { + return supportsOidcLoginCallsCount > 0 } - var eventIdUnderlyingReturnValue: String? - open var eventIdReturnValue: String? { + var supportsOidcLoginUnderlyingReturnValue: Bool! + open var supportsOidcLoginReturnValue: Bool! { get { if Thread.isMainThread { - return eventIdUnderlyingReturnValue + return supportsOidcLoginUnderlyingReturnValue } else { - var returnValue: String?? = nil + var returnValue: Bool? = nil DispatchQueue.main.sync { - returnValue = eventIdUnderlyingReturnValue + returnValue = supportsOidcLoginUnderlyingReturnValue } return returnValue! @@ -6956,36 +6984,36 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - eventIdUnderlyingReturnValue = newValue + supportsOidcLoginUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - eventIdUnderlyingReturnValue = newValue + supportsOidcLoginUnderlyingReturnValue = newValue } } } } - open var eventIdClosure: (() -> String?)? + open var supportsOidcLoginClosure: (() -> Bool)? - open override func eventId() -> String? { - eventIdCallsCount += 1 - if let eventIdClosure = eventIdClosure { - return eventIdClosure() + open override func supportsOidcLogin() -> Bool { + supportsOidcLoginCallsCount += 1 + if let supportsOidcLoginClosure = supportsOidcLoginClosure { + return supportsOidcLoginClosure() } else { - return eventIdReturnValue + return supportsOidcLoginReturnValue } } - //MARK: - getShield + //MARK: - supportsPasswordLogin - var getShieldStrictUnderlyingCallsCount = 0 - open var getShieldStrictCallsCount: Int { + var supportsPasswordLoginUnderlyingCallsCount = 0 + open var supportsPasswordLoginCallsCount: Int { get { if Thread.isMainThread { - return getShieldStrictUnderlyingCallsCount + return supportsPasswordLoginUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = getShieldStrictUnderlyingCallsCount + returnValue = supportsPasswordLoginUnderlyingCallsCount } return returnValue! @@ -6993,29 +7021,27 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - getShieldStrictUnderlyingCallsCount = newValue + supportsPasswordLoginUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - getShieldStrictUnderlyingCallsCount = newValue + supportsPasswordLoginUnderlyingCallsCount = newValue } } } } - open var getShieldStrictCalled: Bool { - return getShieldStrictCallsCount > 0 + open var supportsPasswordLoginCalled: Bool { + return supportsPasswordLoginCallsCount > 0 } - open var getShieldStrictReceivedStrict: Bool? - open var getShieldStrictReceivedInvocations: [Bool] = [] - var getShieldStrictUnderlyingReturnValue: ShieldState? - open var getShieldStrictReturnValue: ShieldState? { + var supportsPasswordLoginUnderlyingReturnValue: Bool! + open var supportsPasswordLoginReturnValue: Bool! { get { if Thread.isMainThread { - return getShieldStrictUnderlyingReturnValue + return supportsPasswordLoginUnderlyingReturnValue } else { - var returnValue: ShieldState?? = nil + var returnValue: Bool? = nil DispatchQueue.main.sync { - returnValue = getShieldStrictUnderlyingReturnValue + returnValue = supportsPasswordLoginUnderlyingReturnValue } return returnValue! @@ -7023,40 +7049,36 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - getShieldStrictUnderlyingReturnValue = newValue + supportsPasswordLoginUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - getShieldStrictUnderlyingReturnValue = newValue + supportsPasswordLoginUnderlyingReturnValue = newValue } } } } - open var getShieldStrictClosure: ((Bool) -> ShieldState?)? + open var supportsPasswordLoginClosure: (() -> Bool)? - open override func getShield(strict: Bool) -> ShieldState? { - getShieldStrictCallsCount += 1 - getShieldStrictReceivedStrict = strict - DispatchQueue.main.async { - self.getShieldStrictReceivedInvocations.append(strict) - } - if let getShieldStrictClosure = getShieldStrictClosure { - return getShieldStrictClosure(strict) + open override func supportsPasswordLogin() -> Bool { + supportsPasswordLoginCallsCount += 1 + if let supportsPasswordLoginClosure = supportsPasswordLoginClosure { + return supportsPasswordLoginClosure() } else { - return getShieldStrictReturnValue + return supportsPasswordLoginReturnValue } } - //MARK: - isEditable + //MARK: - url - var isEditableUnderlyingCallsCount = 0 - open var isEditableCallsCount: Int { + var urlUnderlyingCallsCount = 0 + open var urlCallsCount: Int { get { if Thread.isMainThread { - return isEditableUnderlyingCallsCount + return urlUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = isEditableUnderlyingCallsCount + returnValue = urlUnderlyingCallsCount } return returnValue! @@ -7064,27 +7086,27 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - isEditableUnderlyingCallsCount = newValue + urlUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - isEditableUnderlyingCallsCount = newValue + urlUnderlyingCallsCount = newValue } } } } - open var isEditableCalled: Bool { - return isEditableCallsCount > 0 + open var urlCalled: Bool { + return urlCallsCount > 0 } - var isEditableUnderlyingReturnValue: Bool! - open var isEditableReturnValue: Bool! { + var urlUnderlyingReturnValue: String! + open var urlReturnValue: String! { get { if Thread.isMainThread { - return isEditableUnderlyingReturnValue + return urlUnderlyingReturnValue } else { - var returnValue: Bool? = nil + var returnValue: String? = nil DispatchQueue.main.sync { - returnValue = isEditableUnderlyingReturnValue + returnValue = urlUnderlyingReturnValue } return returnValue! @@ -7092,36 +7114,47 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - isEditableUnderlyingReturnValue = newValue + urlUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - isEditableUnderlyingReturnValue = newValue + urlUnderlyingReturnValue = newValue } } } } - open var isEditableClosure: (() -> Bool)? + open var urlClosure: (() -> String)? - open override func isEditable() -> Bool { - isEditableCallsCount += 1 - if let isEditableClosure = isEditableClosure { - return isEditableClosure() + open override func url() -> String { + urlCallsCount += 1 + if let urlClosure = urlClosure { + return urlClosure() } else { - return isEditableReturnValue + return urlReturnValue } } +} +open class IdentityResetHandleSDKMock: MatrixRustSDK.IdentityResetHandle { + init() { + super.init(noPointer: .init()) + } + + public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + fatalError("init(unsafeFromRawPointer:) has not been implemented") + } + + fileprivate var pointer: UnsafeMutableRawPointer! - //MARK: - isLocal + //MARK: - authType - var isLocalUnderlyingCallsCount = 0 - open var isLocalCallsCount: Int { + var authTypeUnderlyingCallsCount = 0 + open var authTypeCallsCount: Int { get { if Thread.isMainThread { - return isLocalUnderlyingCallsCount + return authTypeUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = isLocalUnderlyingCallsCount + returnValue = authTypeUnderlyingCallsCount } return returnValue! @@ -7129,27 +7162,27 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - isLocalUnderlyingCallsCount = newValue + authTypeUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - isLocalUnderlyingCallsCount = newValue + authTypeUnderlyingCallsCount = newValue } } } } - open var isLocalCalled: Bool { - return isLocalCallsCount > 0 + open var authTypeCalled: Bool { + return authTypeCallsCount > 0 } - var isLocalUnderlyingReturnValue: Bool! - open var isLocalReturnValue: Bool! { + var authTypeUnderlyingReturnValue: CrossSigningResetAuthType! + open var authTypeReturnValue: CrossSigningResetAuthType! { get { if Thread.isMainThread { - return isLocalUnderlyingReturnValue + return authTypeUnderlyingReturnValue } else { - var returnValue: Bool? = nil + var returnValue: CrossSigningResetAuthType? = nil DispatchQueue.main.sync { - returnValue = isLocalUnderlyingReturnValue + returnValue = authTypeUnderlyingReturnValue } return returnValue! @@ -7157,36 +7190,36 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - isLocalUnderlyingReturnValue = newValue + authTypeUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - isLocalUnderlyingReturnValue = newValue + authTypeUnderlyingReturnValue = newValue } } } } - open var isLocalClosure: (() -> Bool)? + open var authTypeClosure: (() -> CrossSigningResetAuthType)? - open override func isLocal() -> Bool { - isLocalCallsCount += 1 - if let isLocalClosure = isLocalClosure { - return isLocalClosure() + open override func authType() -> CrossSigningResetAuthType { + authTypeCallsCount += 1 + if let authTypeClosure = authTypeClosure { + return authTypeClosure() } else { - return isLocalReturnValue + return authTypeReturnValue } } - //MARK: - isOwn + //MARK: - cancel - var isOwnUnderlyingCallsCount = 0 - open var isOwnCallsCount: Int { + var cancelUnderlyingCallsCount = 0 + open var cancelCallsCount: Int { get { if Thread.isMainThread { - return isOwnUnderlyingCallsCount + return cancelUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = isOwnUnderlyingCallsCount + returnValue = cancelUnderlyingCallsCount } return returnValue! @@ -7194,27 +7227,36 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - isOwnUnderlyingCallsCount = newValue + cancelUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - isOwnUnderlyingCallsCount = newValue + cancelUnderlyingCallsCount = newValue } } } } - open var isOwnCalled: Bool { - return isOwnCallsCount > 0 + open var cancelCalled: Bool { + return cancelCallsCount > 0 + } + open var cancelClosure: (() async -> Void)? + + open override func cancel() async { + cancelCallsCount += 1 + await cancelClosure?() } - var isOwnUnderlyingReturnValue: Bool! - open var isOwnReturnValue: Bool! { + //MARK: - reset + + open var resetAuthThrowableError: Error? + var resetAuthUnderlyingCallsCount = 0 + open var resetAuthCallsCount: Int { get { if Thread.isMainThread { - return isOwnUnderlyingReturnValue + return resetAuthUnderlyingCallsCount } else { - var returnValue: Bool? = nil + var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = isOwnUnderlyingReturnValue + returnValue = resetAuthUnderlyingCallsCount } return returnValue! @@ -7222,36 +7264,55 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - isOwnUnderlyingReturnValue = newValue + resetAuthUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - isOwnUnderlyingReturnValue = newValue + resetAuthUnderlyingCallsCount = newValue } } } } - open var isOwnClosure: (() -> Bool)? + open var resetAuthCalled: Bool { + return resetAuthCallsCount > 0 + } + open var resetAuthReceivedAuth: AuthData? + open var resetAuthReceivedInvocations: [AuthData?] = [] + open var resetAuthClosure: ((AuthData?) async throws -> Void)? - open override func isOwn() -> Bool { - isOwnCallsCount += 1 - if let isOwnClosure = isOwnClosure { - return isOwnClosure() - } else { - return isOwnReturnValue + open override func reset(auth: AuthData?) async throws { + if let error = resetAuthThrowableError { + throw error + } + resetAuthCallsCount += 1 + resetAuthReceivedAuth = auth + DispatchQueue.main.async { + self.resetAuthReceivedInvocations.append(auth) } + try await resetAuthClosure?(auth) + } +} +open class InReplyToDetailsSDKMock: MatrixRustSDK.InReplyToDetails { + init() { + super.init(noPointer: .init()) + } + + public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + fatalError("init(unsafeFromRawPointer:) has not been implemented") } - //MARK: - isRemote + fileprivate var pointer: UnsafeMutableRawPointer! + + //MARK: - event - var isRemoteUnderlyingCallsCount = 0 - open var isRemoteCallsCount: Int { + var eventUnderlyingCallsCount = 0 + open var eventCallsCount: Int { get { if Thread.isMainThread { - return isRemoteUnderlyingCallsCount + return eventUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = isRemoteUnderlyingCallsCount + returnValue = eventUnderlyingCallsCount } return returnValue! @@ -7259,27 +7320,27 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - isRemoteUnderlyingCallsCount = newValue + eventUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - isRemoteUnderlyingCallsCount = newValue + eventUnderlyingCallsCount = newValue } } } } - open var isRemoteCalled: Bool { - return isRemoteCallsCount > 0 + open var eventCalled: Bool { + return eventCallsCount > 0 } - var isRemoteUnderlyingReturnValue: Bool! - open var isRemoteReturnValue: Bool! { + var eventUnderlyingReturnValue: RepliedToEventDetails! + open var eventReturnValue: RepliedToEventDetails! { get { if Thread.isMainThread { - return isRemoteUnderlyingReturnValue + return eventUnderlyingReturnValue } else { - var returnValue: Bool? = nil + var returnValue: RepliedToEventDetails? = nil DispatchQueue.main.sync { - returnValue = isRemoteUnderlyingReturnValue + returnValue = eventUnderlyingReturnValue } return returnValue! @@ -7287,36 +7348,36 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - isRemoteUnderlyingReturnValue = newValue + eventUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - isRemoteUnderlyingReturnValue = newValue + eventUnderlyingReturnValue = newValue } } } } - open var isRemoteClosure: (() -> Bool)? + open var eventClosure: (() -> RepliedToEventDetails)? - open override func isRemote() -> Bool { - isRemoteCallsCount += 1 - if let isRemoteClosure = isRemoteClosure { - return isRemoteClosure() + open override func event() -> RepliedToEventDetails { + eventCallsCount += 1 + if let eventClosure = eventClosure { + return eventClosure() } else { - return isRemoteReturnValue + return eventReturnValue } } - //MARK: - localSendState + //MARK: - eventId - var localSendStateUnderlyingCallsCount = 0 - open var localSendStateCallsCount: Int { + var eventIdUnderlyingCallsCount = 0 + open var eventIdCallsCount: Int { get { if Thread.isMainThread { - return localSendStateUnderlyingCallsCount + return eventIdUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = localSendStateUnderlyingCallsCount + returnValue = eventIdUnderlyingCallsCount } return returnValue! @@ -7324,27 +7385,27 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - localSendStateUnderlyingCallsCount = newValue + eventIdUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - localSendStateUnderlyingCallsCount = newValue + eventIdUnderlyingCallsCount = newValue } } } } - open var localSendStateCalled: Bool { - return localSendStateCallsCount > 0 + open var eventIdCalled: Bool { + return eventIdCallsCount > 0 } - var localSendStateUnderlyingReturnValue: EventSendState? - open var localSendStateReturnValue: EventSendState? { + var eventIdUnderlyingReturnValue: String! + open var eventIdReturnValue: String! { get { if Thread.isMainThread { - return localSendStateUnderlyingReturnValue + return eventIdUnderlyingReturnValue } else { - var returnValue: EventSendState?? = nil + var returnValue: String? = nil DispatchQueue.main.sync { - returnValue = localSendStateUnderlyingReturnValue + returnValue = eventIdUnderlyingReturnValue } return returnValue! @@ -7352,1230 +7413,48 @@ open class EventTimelineItemSDKMock: MatrixRustSDK.EventTimelineItem { } set { if Thread.isMainThread { - localSendStateUnderlyingReturnValue = newValue + eventIdUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - localSendStateUnderlyingReturnValue = newValue + eventIdUnderlyingReturnValue = newValue } } } } - open var localSendStateClosure: (() -> EventSendState?)? + open var eventIdClosure: (() -> String)? - open override func localSendState() -> EventSendState? { - localSendStateCallsCount += 1 - if let localSendStateClosure = localSendStateClosure { - return localSendStateClosure() + open override func eventId() -> String { + eventIdCallsCount += 1 + if let eventIdClosure = eventIdClosure { + return eventIdClosure() } else { - return localSendStateReturnValue + return eventIdReturnValue } } +} +open class MediaFileHandleSDKMock: MatrixRustSDK.MediaFileHandle { + init() { + super.init(noPointer: .init()) + } - //MARK: - origin - - var originUnderlyingCallsCount = 0 - open var originCallsCount: Int { - get { - if Thread.isMainThread { - return originUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = originUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - originUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - originUnderlyingCallsCount = newValue - } - } - } - } - open var originCalled: Bool { - return originCallsCount > 0 - } - - var originUnderlyingReturnValue: EventItemOrigin? - open var originReturnValue: EventItemOrigin? { - get { - if Thread.isMainThread { - return originUnderlyingReturnValue - } else { - var returnValue: EventItemOrigin?? = nil - DispatchQueue.main.sync { - returnValue = originUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - originUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - originUnderlyingReturnValue = newValue - } - } - } - } - open var originClosure: (() -> EventItemOrigin?)? - - open override func origin() -> EventItemOrigin? { - originCallsCount += 1 - if let originClosure = originClosure { - return originClosure() - } else { - return originReturnValue - } - } - - //MARK: - reactions - - var reactionsUnderlyingCallsCount = 0 - open var reactionsCallsCount: Int { - get { - if Thread.isMainThread { - return reactionsUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = reactionsUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - reactionsUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - reactionsUnderlyingCallsCount = newValue - } - } - } - } - open var reactionsCalled: Bool { - return reactionsCallsCount > 0 - } - - var reactionsUnderlyingReturnValue: [Reaction]! - open var reactionsReturnValue: [Reaction]! { - get { - if Thread.isMainThread { - return reactionsUnderlyingReturnValue - } else { - var returnValue: [Reaction]? = nil - DispatchQueue.main.sync { - returnValue = reactionsUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - reactionsUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - reactionsUnderlyingReturnValue = newValue - } - } - } - } - open var reactionsClosure: (() -> [Reaction])? - - open override func reactions() -> [Reaction] { - reactionsCallsCount += 1 - if let reactionsClosure = reactionsClosure { - return reactionsClosure() - } else { - return reactionsReturnValue - } - } - - //MARK: - readReceipts - - var readReceiptsUnderlyingCallsCount = 0 - open var readReceiptsCallsCount: Int { - get { - if Thread.isMainThread { - return readReceiptsUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = readReceiptsUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - readReceiptsUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - readReceiptsUnderlyingCallsCount = newValue - } - } - } - } - open var readReceiptsCalled: Bool { - return readReceiptsCallsCount > 0 - } - - var readReceiptsUnderlyingReturnValue: [String: Receipt]! - open var readReceiptsReturnValue: [String: Receipt]! { - get { - if Thread.isMainThread { - return readReceiptsUnderlyingReturnValue - } else { - var returnValue: [String: Receipt]? = nil - DispatchQueue.main.sync { - returnValue = readReceiptsUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - readReceiptsUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - readReceiptsUnderlyingReturnValue = newValue - } - } - } - } - open var readReceiptsClosure: (() -> [String: Receipt])? - - open override func readReceipts() -> [String: Receipt] { - readReceiptsCallsCount += 1 - if let readReceiptsClosure = readReceiptsClosure { - return readReceiptsClosure() - } else { - return readReceiptsReturnValue - } - } - - //MARK: - sender - - var senderUnderlyingCallsCount = 0 - open var senderCallsCount: Int { - get { - if Thread.isMainThread { - return senderUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = senderUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - senderUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - senderUnderlyingCallsCount = newValue - } - } - } - } - open var senderCalled: Bool { - return senderCallsCount > 0 - } - - var senderUnderlyingReturnValue: String! - open var senderReturnValue: String! { - get { - if Thread.isMainThread { - return senderUnderlyingReturnValue - } else { - var returnValue: String? = nil - DispatchQueue.main.sync { - returnValue = senderUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - senderUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - senderUnderlyingReturnValue = newValue - } - } - } - } - open var senderClosure: (() -> String)? - - open override func sender() -> String { - senderCallsCount += 1 - if let senderClosure = senderClosure { - return senderClosure() - } else { - return senderReturnValue - } - } - - //MARK: - senderProfile - - var senderProfileUnderlyingCallsCount = 0 - open var senderProfileCallsCount: Int { - get { - if Thread.isMainThread { - return senderProfileUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = senderProfileUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - senderProfileUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - senderProfileUnderlyingCallsCount = newValue - } - } - } - } - open var senderProfileCalled: Bool { - return senderProfileCallsCount > 0 - } - - var senderProfileUnderlyingReturnValue: ProfileDetails! - open var senderProfileReturnValue: ProfileDetails! { - get { - if Thread.isMainThread { - return senderProfileUnderlyingReturnValue - } else { - var returnValue: ProfileDetails? = nil - DispatchQueue.main.sync { - returnValue = senderProfileUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - senderProfileUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - senderProfileUnderlyingReturnValue = newValue - } - } - } - } - open var senderProfileClosure: (() -> ProfileDetails)? - - open override func senderProfile() -> ProfileDetails { - senderProfileCallsCount += 1 - if let senderProfileClosure = senderProfileClosure { - return senderProfileClosure() - } else { - return senderProfileReturnValue - } - } - - //MARK: - timestamp - - var timestampUnderlyingCallsCount = 0 - open var timestampCallsCount: Int { - get { - if Thread.isMainThread { - return timestampUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = timestampUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - timestampUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - timestampUnderlyingCallsCount = newValue - } - } - } - } - open var timestampCalled: Bool { - return timestampCallsCount > 0 - } - - var timestampUnderlyingReturnValue: UInt64! - open var timestampReturnValue: UInt64! { - get { - if Thread.isMainThread { - return timestampUnderlyingReturnValue - } else { - var returnValue: UInt64? = nil - DispatchQueue.main.sync { - returnValue = timestampUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - timestampUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - timestampUnderlyingReturnValue = newValue - } - } - } - } - open var timestampClosure: (() -> UInt64)? - - open override func timestamp() -> UInt64 { - timestampCallsCount += 1 - if let timestampClosure = timestampClosure { - return timestampClosure() - } else { - return timestampReturnValue - } - } - - //MARK: - transactionId - - var transactionIdUnderlyingCallsCount = 0 - open var transactionIdCallsCount: Int { - get { - if Thread.isMainThread { - return transactionIdUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = transactionIdUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - transactionIdUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - transactionIdUnderlyingCallsCount = newValue - } - } - } - } - open var transactionIdCalled: Bool { - return transactionIdCallsCount > 0 - } - - var transactionIdUnderlyingReturnValue: String? - open var transactionIdReturnValue: String? { - get { - if Thread.isMainThread { - return transactionIdUnderlyingReturnValue - } else { - var returnValue: String?? = nil - DispatchQueue.main.sync { - returnValue = transactionIdUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - transactionIdUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - transactionIdUnderlyingReturnValue = newValue - } - } - } - } - open var transactionIdClosure: (() -> String?)? - - open override func transactionId() -> String? { - transactionIdCallsCount += 1 - if let transactionIdClosure = transactionIdClosure { - return transactionIdClosure() - } else { - return transactionIdReturnValue - } - } -} -open class HomeserverLoginDetailsSDKMock: MatrixRustSDK.HomeserverLoginDetails { - init() { - super.init(noPointer: .init()) - } - - public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { - fatalError("init(unsafeFromRawPointer:) has not been implemented") - } - - fileprivate var pointer: UnsafeMutableRawPointer! - - //MARK: - slidingSyncVersion - - var slidingSyncVersionUnderlyingCallsCount = 0 - open var slidingSyncVersionCallsCount: Int { - get { - if Thread.isMainThread { - return slidingSyncVersionUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = slidingSyncVersionUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - slidingSyncVersionUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - slidingSyncVersionUnderlyingCallsCount = newValue - } - } - } - } - open var slidingSyncVersionCalled: Bool { - return slidingSyncVersionCallsCount > 0 - } - - var slidingSyncVersionUnderlyingReturnValue: SlidingSyncVersion! - open var slidingSyncVersionReturnValue: SlidingSyncVersion! { - get { - if Thread.isMainThread { - return slidingSyncVersionUnderlyingReturnValue - } else { - var returnValue: SlidingSyncVersion? = nil - DispatchQueue.main.sync { - returnValue = slidingSyncVersionUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - slidingSyncVersionUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - slidingSyncVersionUnderlyingReturnValue = newValue - } - } - } - } - open var slidingSyncVersionClosure: (() -> SlidingSyncVersion)? - - open override func slidingSyncVersion() -> SlidingSyncVersion { - slidingSyncVersionCallsCount += 1 - if let slidingSyncVersionClosure = slidingSyncVersionClosure { - return slidingSyncVersionClosure() - } else { - return slidingSyncVersionReturnValue - } - } - - //MARK: - supportsOidcLogin - - var supportsOidcLoginUnderlyingCallsCount = 0 - open var supportsOidcLoginCallsCount: Int { - get { - if Thread.isMainThread { - return supportsOidcLoginUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = supportsOidcLoginUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - supportsOidcLoginUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - supportsOidcLoginUnderlyingCallsCount = newValue - } - } - } - } - open var supportsOidcLoginCalled: Bool { - return supportsOidcLoginCallsCount > 0 - } - - var supportsOidcLoginUnderlyingReturnValue: Bool! - open var supportsOidcLoginReturnValue: Bool! { - get { - if Thread.isMainThread { - return supportsOidcLoginUnderlyingReturnValue - } else { - var returnValue: Bool? = nil - DispatchQueue.main.sync { - returnValue = supportsOidcLoginUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - supportsOidcLoginUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - supportsOidcLoginUnderlyingReturnValue = newValue - } - } - } - } - open var supportsOidcLoginClosure: (() -> Bool)? - - open override func supportsOidcLogin() -> Bool { - supportsOidcLoginCallsCount += 1 - if let supportsOidcLoginClosure = supportsOidcLoginClosure { - return supportsOidcLoginClosure() - } else { - return supportsOidcLoginReturnValue - } - } - - //MARK: - supportsPasswordLogin - - var supportsPasswordLoginUnderlyingCallsCount = 0 - open var supportsPasswordLoginCallsCount: Int { - get { - if Thread.isMainThread { - return supportsPasswordLoginUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = supportsPasswordLoginUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - supportsPasswordLoginUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - supportsPasswordLoginUnderlyingCallsCount = newValue - } - } - } - } - open var supportsPasswordLoginCalled: Bool { - return supportsPasswordLoginCallsCount > 0 - } - - var supportsPasswordLoginUnderlyingReturnValue: Bool! - open var supportsPasswordLoginReturnValue: Bool! { - get { - if Thread.isMainThread { - return supportsPasswordLoginUnderlyingReturnValue - } else { - var returnValue: Bool? = nil - DispatchQueue.main.sync { - returnValue = supportsPasswordLoginUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - supportsPasswordLoginUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - supportsPasswordLoginUnderlyingReturnValue = newValue - } - } - } - } - open var supportsPasswordLoginClosure: (() -> Bool)? - - open override func supportsPasswordLogin() -> Bool { - supportsPasswordLoginCallsCount += 1 - if let supportsPasswordLoginClosure = supportsPasswordLoginClosure { - return supportsPasswordLoginClosure() - } else { - return supportsPasswordLoginReturnValue - } - } - - //MARK: - url - - var urlUnderlyingCallsCount = 0 - open var urlCallsCount: Int { - get { - if Thread.isMainThread { - return urlUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = urlUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - urlUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - urlUnderlyingCallsCount = newValue - } - } - } - } - open var urlCalled: Bool { - return urlCallsCount > 0 - } - - var urlUnderlyingReturnValue: String! - open var urlReturnValue: String! { - get { - if Thread.isMainThread { - return urlUnderlyingReturnValue - } else { - var returnValue: String? = nil - DispatchQueue.main.sync { - returnValue = urlUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - urlUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - urlUnderlyingReturnValue = newValue - } - } - } - } - open var urlClosure: (() -> String)? - - open override func url() -> String { - urlCallsCount += 1 - if let urlClosure = urlClosure { - return urlClosure() - } else { - return urlReturnValue - } - } -} -open class IdentityResetHandleSDKMock: MatrixRustSDK.IdentityResetHandle { - init() { - super.init(noPointer: .init()) - } - - public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { - fatalError("init(unsafeFromRawPointer:) has not been implemented") - } - - fileprivate var pointer: UnsafeMutableRawPointer! - - //MARK: - authType - - var authTypeUnderlyingCallsCount = 0 - open var authTypeCallsCount: Int { - get { - if Thread.isMainThread { - return authTypeUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = authTypeUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - authTypeUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - authTypeUnderlyingCallsCount = newValue - } - } - } - } - open var authTypeCalled: Bool { - return authTypeCallsCount > 0 - } - - var authTypeUnderlyingReturnValue: CrossSigningResetAuthType! - open var authTypeReturnValue: CrossSigningResetAuthType! { - get { - if Thread.isMainThread { - return authTypeUnderlyingReturnValue - } else { - var returnValue: CrossSigningResetAuthType? = nil - DispatchQueue.main.sync { - returnValue = authTypeUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - authTypeUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - authTypeUnderlyingReturnValue = newValue - } - } - } - } - open var authTypeClosure: (() -> CrossSigningResetAuthType)? - - open override func authType() -> CrossSigningResetAuthType { - authTypeCallsCount += 1 - if let authTypeClosure = authTypeClosure { - return authTypeClosure() - } else { - return authTypeReturnValue - } - } - - //MARK: - cancel - - var cancelUnderlyingCallsCount = 0 - open var cancelCallsCount: Int { - get { - if Thread.isMainThread { - return cancelUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = cancelUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - cancelUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - cancelUnderlyingCallsCount = newValue - } - } - } - } - open var cancelCalled: Bool { - return cancelCallsCount > 0 - } - open var cancelClosure: (() async -> Void)? - - open override func cancel() async { - cancelCallsCount += 1 - await cancelClosure?() - } - - //MARK: - reset - - open var resetAuthThrowableError: Error? - var resetAuthUnderlyingCallsCount = 0 - open var resetAuthCallsCount: Int { - get { - if Thread.isMainThread { - return resetAuthUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = resetAuthUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - resetAuthUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - resetAuthUnderlyingCallsCount = newValue - } - } - } - } - open var resetAuthCalled: Bool { - return resetAuthCallsCount > 0 - } - open var resetAuthReceivedAuth: AuthData? - open var resetAuthReceivedInvocations: [AuthData?] = [] - open var resetAuthClosure: ((AuthData?) async throws -> Void)? - - open override func reset(auth: AuthData?) async throws { - if let error = resetAuthThrowableError { - throw error - } - resetAuthCallsCount += 1 - resetAuthReceivedAuth = auth - DispatchQueue.main.async { - self.resetAuthReceivedInvocations.append(auth) - } - try await resetAuthClosure?(auth) - } -} -open class MediaFileHandleSDKMock: MatrixRustSDK.MediaFileHandle { - init() { - super.init(noPointer: .init()) - } - - public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { - fatalError("init(unsafeFromRawPointer:) has not been implemented") - } - - fileprivate var pointer: UnsafeMutableRawPointer! - - //MARK: - path - - open var pathThrowableError: Error? - var pathUnderlyingCallsCount = 0 - open var pathCallsCount: Int { - get { - if Thread.isMainThread { - return pathUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = pathUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - pathUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - pathUnderlyingCallsCount = newValue - } - } - } - } - open var pathCalled: Bool { - return pathCallsCount > 0 - } - - var pathUnderlyingReturnValue: String! - open var pathReturnValue: String! { - get { - if Thread.isMainThread { - return pathUnderlyingReturnValue - } else { - var returnValue: String? = nil - DispatchQueue.main.sync { - returnValue = pathUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - pathUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - pathUnderlyingReturnValue = newValue - } - } - } - } - open var pathClosure: (() throws -> String)? - - open override func path() throws -> String { - if let error = pathThrowableError { - throw error - } - pathCallsCount += 1 - if let pathClosure = pathClosure { - return try pathClosure() - } else { - return pathReturnValue - } - } - - //MARK: - persist - - open var persistPathThrowableError: Error? - var persistPathUnderlyingCallsCount = 0 - open var persistPathCallsCount: Int { - get { - if Thread.isMainThread { - return persistPathUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = persistPathUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - persistPathUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - persistPathUnderlyingCallsCount = newValue - } - } - } - } - open var persistPathCalled: Bool { - return persistPathCallsCount > 0 - } - open var persistPathReceivedPath: String? - open var persistPathReceivedInvocations: [String] = [] - - var persistPathUnderlyingReturnValue: Bool! - open var persistPathReturnValue: Bool! { - get { - if Thread.isMainThread { - return persistPathUnderlyingReturnValue - } else { - var returnValue: Bool? = nil - DispatchQueue.main.sync { - returnValue = persistPathUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - persistPathUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - persistPathUnderlyingReturnValue = newValue - } - } - } - } - open var persistPathClosure: ((String) throws -> Bool)? - - open override func persist(path: String) throws -> Bool { - if let error = persistPathThrowableError { - throw error - } - persistPathCallsCount += 1 - persistPathReceivedPath = path - DispatchQueue.main.async { - self.persistPathReceivedInvocations.append(path) - } - if let persistPathClosure = persistPathClosure { - return try persistPathClosure(path) - } else { - return persistPathReturnValue - } - } -} -open class MediaSourceSDKMock: MatrixRustSDK.MediaSource { - init() { - super.init(noPointer: .init()) - } - - public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { - fatalError("init(unsafeFromRawPointer:) has not been implemented") - } - - fileprivate var pointer: UnsafeMutableRawPointer! - static func reset() - { - } - - //MARK: - toJson - - var toJsonUnderlyingCallsCount = 0 - open var toJsonCallsCount: Int { - get { - if Thread.isMainThread { - return toJsonUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = toJsonUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - toJsonUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - toJsonUnderlyingCallsCount = newValue - } - } - } - } - open var toJsonCalled: Bool { - return toJsonCallsCount > 0 - } - - var toJsonUnderlyingReturnValue: String! - open var toJsonReturnValue: String! { - get { - if Thread.isMainThread { - return toJsonUnderlyingReturnValue - } else { - var returnValue: String? = nil - DispatchQueue.main.sync { - returnValue = toJsonUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - toJsonUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - toJsonUnderlyingReturnValue = newValue - } - } - } - } - open var toJsonClosure: (() -> String)? - - open override func toJson() -> String { - toJsonCallsCount += 1 - if let toJsonClosure = toJsonClosure { - return toJsonClosure() - } else { - return toJsonReturnValue - } - } - - //MARK: - url - - var urlUnderlyingCallsCount = 0 - open var urlCallsCount: Int { - get { - if Thread.isMainThread { - return urlUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = urlUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - urlUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - urlUnderlyingCallsCount = newValue - } - } - } - } - open var urlCalled: Bool { - return urlCallsCount > 0 - } - - var urlUnderlyingReturnValue: String! - open var urlReturnValue: String! { - get { - if Thread.isMainThread { - return urlUnderlyingReturnValue - } else { - var returnValue: String? = nil - DispatchQueue.main.sync { - returnValue = urlUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - urlUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - urlUnderlyingReturnValue = newValue - } - } - } - } - open var urlClosure: (() -> String)? - - open override func url() -> String { - urlCallsCount += 1 - if let urlClosure = urlClosure { - return urlClosure() - } else { - return urlReturnValue - } - } -} -open class MessageSDKMock: MatrixRustSDK.Message { - init() { - super.init(noPointer: .init()) - } - - public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { - fatalError("init(unsafeFromRawPointer:) has not been implemented") - } + public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + fatalError("init(unsafeFromRawPointer:) has not been implemented") + } fileprivate var pointer: UnsafeMutableRawPointer! - //MARK: - body + //MARK: - path - var bodyUnderlyingCallsCount = 0 - open var bodyCallsCount: Int { + open var pathThrowableError: Error? + var pathUnderlyingCallsCount = 0 + open var pathCallsCount: Int { get { if Thread.isMainThread { - return bodyUnderlyingCallsCount + return pathUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = bodyUnderlyingCallsCount + returnValue = pathUnderlyingCallsCount } return returnValue! @@ -8583,129 +7462,27 @@ open class MessageSDKMock: MatrixRustSDK.Message { } set { if Thread.isMainThread { - bodyUnderlyingCallsCount = newValue + pathUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - bodyUnderlyingCallsCount = newValue + pathUnderlyingCallsCount = newValue } } } } - open var bodyCalled: Bool { - return bodyCallsCount > 0 + open var pathCalled: Bool { + return pathCallsCount > 0 } - var bodyUnderlyingReturnValue: String! - open var bodyReturnValue: String! { + var pathUnderlyingReturnValue: String! + open var pathReturnValue: String! { get { if Thread.isMainThread { - return bodyUnderlyingReturnValue + return pathUnderlyingReturnValue } else { var returnValue: String? = nil DispatchQueue.main.sync { - returnValue = bodyUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - bodyUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - bodyUnderlyingReturnValue = newValue - } - } - } - } - open var bodyClosure: (() -> String)? - - open override func body() -> String { - bodyCallsCount += 1 - if let bodyClosure = bodyClosure { - return bodyClosure() - } else { - return bodyReturnValue - } - } - - //MARK: - content - - var contentUnderlyingCallsCount = 0 - open var contentCallsCount: Int { - get { - if Thread.isMainThread { - return contentUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = contentUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - contentUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - contentUnderlyingCallsCount = newValue - } - } - } - } - open var contentCalled: Bool { - return contentCallsCount > 0 - } - - var contentUnderlyingReturnValue: RoomMessageEventContentWithoutRelation! - open var contentReturnValue: RoomMessageEventContentWithoutRelation! { - get { - if Thread.isMainThread { - return contentUnderlyingReturnValue - } else { - var returnValue: RoomMessageEventContentWithoutRelation? = nil - DispatchQueue.main.sync { - returnValue = contentUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - contentUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - contentUnderlyingReturnValue = newValue - } - } - } - } - open var contentClosure: (() -> RoomMessageEventContentWithoutRelation)? - - open override func content() -> RoomMessageEventContentWithoutRelation { - contentCallsCount += 1 - if let contentClosure = contentClosure { - return contentClosure() - } else { - return contentReturnValue - } - } - - //MARK: - inReplyTo - - var inReplyToUnderlyingCallsCount = 0 - open var inReplyToCallsCount: Int { - get { - if Thread.isMainThread { - return inReplyToUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = inReplyToUnderlyingCallsCount + returnValue = pathUnderlyingReturnValue } return returnValue! @@ -8713,64 +7490,40 @@ open class MessageSDKMock: MatrixRustSDK.Message { } set { if Thread.isMainThread { - inReplyToUnderlyingCallsCount = newValue + pathUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - inReplyToUnderlyingCallsCount = newValue + pathUnderlyingReturnValue = newValue } } } } - open var inReplyToCalled: Bool { - return inReplyToCallsCount > 0 - } - - var inReplyToUnderlyingReturnValue: InReplyToDetails? - open var inReplyToReturnValue: InReplyToDetails? { - get { - if Thread.isMainThread { - return inReplyToUnderlyingReturnValue - } else { - var returnValue: InReplyToDetails?? = nil - DispatchQueue.main.sync { - returnValue = inReplyToUnderlyingReturnValue - } + open var pathClosure: (() throws -> String)? - return returnValue! - } - } - set { - if Thread.isMainThread { - inReplyToUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - inReplyToUnderlyingReturnValue = newValue - } - } + open override func path() throws -> String { + if let error = pathThrowableError { + throw error } - } - open var inReplyToClosure: (() -> InReplyToDetails?)? - - open override func inReplyTo() -> InReplyToDetails? { - inReplyToCallsCount += 1 - if let inReplyToClosure = inReplyToClosure { - return inReplyToClosure() + pathCallsCount += 1 + if let pathClosure = pathClosure { + return try pathClosure() } else { - return inReplyToReturnValue + return pathReturnValue } } - //MARK: - isEdited + //MARK: - persist - var isEditedUnderlyingCallsCount = 0 - open var isEditedCallsCount: Int { + open var persistPathThrowableError: Error? + var persistPathUnderlyingCallsCount = 0 + open var persistPathCallsCount: Int { get { if Thread.isMainThread { - return isEditedUnderlyingCallsCount + return persistPathUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = isEditedUnderlyingCallsCount + returnValue = persistPathUnderlyingCallsCount } return returnValue! @@ -8778,27 +7531,29 @@ open class MessageSDKMock: MatrixRustSDK.Message { } set { if Thread.isMainThread { - isEditedUnderlyingCallsCount = newValue + persistPathUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - isEditedUnderlyingCallsCount = newValue + persistPathUnderlyingCallsCount = newValue } } } } - open var isEditedCalled: Bool { - return isEditedCallsCount > 0 + open var persistPathCalled: Bool { + return persistPathCallsCount > 0 } + open var persistPathReceivedPath: String? + open var persistPathReceivedInvocations: [String] = [] - var isEditedUnderlyingReturnValue: Bool! - open var isEditedReturnValue: Bool! { + var persistPathUnderlyingReturnValue: Bool! + open var persistPathReturnValue: Bool! { get { if Thread.isMainThread { - return isEditedUnderlyingReturnValue + return persistPathUnderlyingReturnValue } else { var returnValue: Bool? = nil DispatchQueue.main.sync { - returnValue = isEditedUnderlyingReturnValue + returnValue = persistPathUnderlyingReturnValue } return returnValue! @@ -8806,36 +7561,57 @@ open class MessageSDKMock: MatrixRustSDK.Message { } set { if Thread.isMainThread { - isEditedUnderlyingReturnValue = newValue + persistPathUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - isEditedUnderlyingReturnValue = newValue + persistPathUnderlyingReturnValue = newValue } } } } - open var isEditedClosure: (() -> Bool)? + open var persistPathClosure: ((String) throws -> Bool)? - open override func isEdited() -> Bool { - isEditedCallsCount += 1 - if let isEditedClosure = isEditedClosure { - return isEditedClosure() + open override func persist(path: String) throws -> Bool { + if let error = persistPathThrowableError { + throw error + } + persistPathCallsCount += 1 + persistPathReceivedPath = path + DispatchQueue.main.async { + self.persistPathReceivedInvocations.append(path) + } + if let persistPathClosure = persistPathClosure { + return try persistPathClosure(path) } else { - return isEditedReturnValue + return persistPathReturnValue } } +} +open class MediaSourceSDKMock: MatrixRustSDK.MediaSource { + init() { + super.init(noPointer: .init()) + } + + public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + fatalError("init(unsafeFromRawPointer:) has not been implemented") + } + + fileprivate var pointer: UnsafeMutableRawPointer! + static func reset() + { + } - //MARK: - isThreaded + //MARK: - toJson - var isThreadedUnderlyingCallsCount = 0 - open var isThreadedCallsCount: Int { + var toJsonUnderlyingCallsCount = 0 + open var toJsonCallsCount: Int { get { if Thread.isMainThread { - return isThreadedUnderlyingCallsCount + return toJsonUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = isThreadedUnderlyingCallsCount + returnValue = toJsonUnderlyingCallsCount } return returnValue! @@ -8843,27 +7619,27 @@ open class MessageSDKMock: MatrixRustSDK.Message { } set { if Thread.isMainThread { - isThreadedUnderlyingCallsCount = newValue + toJsonUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - isThreadedUnderlyingCallsCount = newValue + toJsonUnderlyingCallsCount = newValue } } } } - open var isThreadedCalled: Bool { - return isThreadedCallsCount > 0 + open var toJsonCalled: Bool { + return toJsonCallsCount > 0 } - var isThreadedUnderlyingReturnValue: Bool! - open var isThreadedReturnValue: Bool! { + var toJsonUnderlyingReturnValue: String! + open var toJsonReturnValue: String! { get { if Thread.isMainThread { - return isThreadedUnderlyingReturnValue + return toJsonUnderlyingReturnValue } else { - var returnValue: Bool? = nil + var returnValue: String? = nil DispatchQueue.main.sync { - returnValue = isThreadedUnderlyingReturnValue + returnValue = toJsonUnderlyingReturnValue } return returnValue! @@ -8871,36 +7647,36 @@ open class MessageSDKMock: MatrixRustSDK.Message { } set { if Thread.isMainThread { - isThreadedUnderlyingReturnValue = newValue + toJsonUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - isThreadedUnderlyingReturnValue = newValue + toJsonUnderlyingReturnValue = newValue } } } } - open var isThreadedClosure: (() -> Bool)? + open var toJsonClosure: (() -> String)? - open override func isThreaded() -> Bool { - isThreadedCallsCount += 1 - if let isThreadedClosure = isThreadedClosure { - return isThreadedClosure() + open override func toJson() -> String { + toJsonCallsCount += 1 + if let toJsonClosure = toJsonClosure { + return toJsonClosure() } else { - return isThreadedReturnValue + return toJsonReturnValue } } - //MARK: - msgtype + //MARK: - url - var msgtypeUnderlyingCallsCount = 0 - open var msgtypeCallsCount: Int { + var urlUnderlyingCallsCount = 0 + open var urlCallsCount: Int { get { if Thread.isMainThread { - return msgtypeUnderlyingCallsCount + return urlUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = msgtypeUnderlyingCallsCount + returnValue = urlUnderlyingCallsCount } return returnValue! @@ -8908,27 +7684,27 @@ open class MessageSDKMock: MatrixRustSDK.Message { } set { if Thread.isMainThread { - msgtypeUnderlyingCallsCount = newValue + urlUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - msgtypeUnderlyingCallsCount = newValue + urlUnderlyingCallsCount = newValue } } } } - open var msgtypeCalled: Bool { - return msgtypeCallsCount > 0 + open var urlCalled: Bool { + return urlCallsCount > 0 } - var msgtypeUnderlyingReturnValue: MessageType! - open var msgtypeReturnValue: MessageType! { + var urlUnderlyingReturnValue: String! + open var urlReturnValue: String! { get { if Thread.isMainThread { - return msgtypeUnderlyingReturnValue + return urlUnderlyingReturnValue } else { - var returnValue: MessageType? = nil + var returnValue: String? = nil DispatchQueue.main.sync { - returnValue = msgtypeUnderlyingReturnValue + returnValue = urlUnderlyingReturnValue } return returnValue! @@ -8936,22 +7712,22 @@ open class MessageSDKMock: MatrixRustSDK.Message { } set { if Thread.isMainThread { - msgtypeUnderlyingReturnValue = newValue + urlUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - msgtypeUnderlyingReturnValue = newValue + urlUnderlyingReturnValue = newValue } } } } - open var msgtypeClosure: (() -> MessageType)? + open var urlClosure: (() -> String)? - open override func msgtype() -> MessageType { - msgtypeCallsCount += 1 - if let msgtypeClosure = msgtypeClosure { - return msgtypeClosure() + open override func url() -> String { + urlCallsCount += 1 + if let urlClosure = urlClosure { + return urlClosure() } else { - return msgtypeReturnValue + return urlReturnValue } } } @@ -18861,6 +17637,77 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } } + //MARK: - createMessageContent + + var createMessageContentMsgTypeUnderlyingCallsCount = 0 + open var createMessageContentMsgTypeCallsCount: Int { + get { + if Thread.isMainThread { + return createMessageContentMsgTypeUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = createMessageContentMsgTypeUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + createMessageContentMsgTypeUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + createMessageContentMsgTypeUnderlyingCallsCount = newValue + } + } + } + } + open var createMessageContentMsgTypeCalled: Bool { + return createMessageContentMsgTypeCallsCount > 0 + } + open var createMessageContentMsgTypeReceivedMsgType: MessageType? + open var createMessageContentMsgTypeReceivedInvocations: [MessageType] = [] + + var createMessageContentMsgTypeUnderlyingReturnValue: RoomMessageEventContentWithoutRelation? + open var createMessageContentMsgTypeReturnValue: RoomMessageEventContentWithoutRelation? { + get { + if Thread.isMainThread { + return createMessageContentMsgTypeUnderlyingReturnValue + } else { + var returnValue: RoomMessageEventContentWithoutRelation?? = nil + DispatchQueue.main.sync { + returnValue = createMessageContentMsgTypeUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + createMessageContentMsgTypeUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + createMessageContentMsgTypeUnderlyingReturnValue = newValue + } + } + } + } + open var createMessageContentMsgTypeClosure: ((MessageType) -> RoomMessageEventContentWithoutRelation?)? + + open override func createMessageContent(msgType: MessageType) -> RoomMessageEventContentWithoutRelation? { + createMessageContentMsgTypeCallsCount += 1 + createMessageContentMsgTypeReceivedMsgType = msgType + DispatchQueue.main.async { + self.createMessageContentMsgTypeReceivedInvocations.append(msgType) + } + if let createMessageContentMsgTypeClosure = createMessageContentMsgTypeClosure { + return createMessageContentMsgTypeClosure(msgType) + } else { + return createMessageContentMsgTypeReturnValue + } + } + //MARK: - createPoll open var createPollQuestionAnswersMaxSelectionsPollKindThrowableError: Error? @@ -18909,16 +17756,16 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { //MARK: - edit - open var editItemNewContentThrowableError: Error? - var editItemNewContentUnderlyingCallsCount = 0 - open var editItemNewContentCallsCount: Int { + open var editEventOrTransactionIdNewContentThrowableError: Error? + var editEventOrTransactionIdNewContentUnderlyingCallsCount = 0 + open var editEventOrTransactionIdNewContentCallsCount: Int { get { if Thread.isMainThread { - return editItemNewContentUnderlyingCallsCount + return editEventOrTransactionIdNewContentUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = editItemNewContentUnderlyingCallsCount + returnValue = editEventOrTransactionIdNewContentUnderlyingCallsCount } return returnValue! @@ -18926,29 +17773,29 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - editItemNewContentUnderlyingCallsCount = newValue + editEventOrTransactionIdNewContentUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - editItemNewContentUnderlyingCallsCount = newValue + editEventOrTransactionIdNewContentUnderlyingCallsCount = newValue } } } } - open var editItemNewContentCalled: Bool { - return editItemNewContentCallsCount > 0 + open var editEventOrTransactionIdNewContentCalled: Bool { + return editEventOrTransactionIdNewContentCallsCount > 0 } - open var editItemNewContentReceivedArguments: (item: EventTimelineItem, newContent: EditedContent)? - open var editItemNewContentReceivedInvocations: [(item: EventTimelineItem, newContent: EditedContent)] = [] + open var editEventOrTransactionIdNewContentReceivedArguments: (eventOrTransactionId: EventOrTransactionId, newContent: EditedContent)? + open var editEventOrTransactionIdNewContentReceivedInvocations: [(eventOrTransactionId: EventOrTransactionId, newContent: EditedContent)] = [] - var editItemNewContentUnderlyingReturnValue: Bool! - open var editItemNewContentReturnValue: Bool! { + var editEventOrTransactionIdNewContentUnderlyingReturnValue: Bool! + open var editEventOrTransactionIdNewContentReturnValue: Bool! { get { if Thread.isMainThread { - return editItemNewContentUnderlyingReturnValue + return editEventOrTransactionIdNewContentUnderlyingReturnValue } else { var returnValue: Bool? = nil DispatchQueue.main.sync { - returnValue = editItemNewContentUnderlyingReturnValue + returnValue = editEventOrTransactionIdNewContentUnderlyingReturnValue } return returnValue! @@ -18956,44 +17803,44 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - editItemNewContentUnderlyingReturnValue = newValue + editEventOrTransactionIdNewContentUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - editItemNewContentUnderlyingReturnValue = newValue + editEventOrTransactionIdNewContentUnderlyingReturnValue = newValue } } } } - open var editItemNewContentClosure: ((EventTimelineItem, EditedContent) async throws -> Bool)? + open var editEventOrTransactionIdNewContentClosure: ((EventOrTransactionId, EditedContent) async throws -> Bool)? - open override func edit(item: EventTimelineItem, newContent: EditedContent) async throws -> Bool { - if let error = editItemNewContentThrowableError { + open override func edit(eventOrTransactionId: EventOrTransactionId, newContent: EditedContent) async throws -> Bool { + if let error = editEventOrTransactionIdNewContentThrowableError { throw error } - editItemNewContentCallsCount += 1 - editItemNewContentReceivedArguments = (item: item, newContent: newContent) + editEventOrTransactionIdNewContentCallsCount += 1 + editEventOrTransactionIdNewContentReceivedArguments = (eventOrTransactionId: eventOrTransactionId, newContent: newContent) DispatchQueue.main.async { - self.editItemNewContentReceivedInvocations.append((item: item, newContent: newContent)) + self.editEventOrTransactionIdNewContentReceivedInvocations.append((eventOrTransactionId: eventOrTransactionId, newContent: newContent)) } - if let editItemNewContentClosure = editItemNewContentClosure { - return try await editItemNewContentClosure(item, newContent) + if let editEventOrTransactionIdNewContentClosure = editEventOrTransactionIdNewContentClosure { + return try await editEventOrTransactionIdNewContentClosure(eventOrTransactionId, newContent) } else { - return editItemNewContentReturnValue + return editEventOrTransactionIdNewContentReturnValue } } //MARK: - endPoll - open var endPollPollStartIdTextThrowableError: Error? - var endPollPollStartIdTextUnderlyingCallsCount = 0 - open var endPollPollStartIdTextCallsCount: Int { + open var endPollPollStartEventIdTextThrowableError: Error? + var endPollPollStartEventIdTextUnderlyingCallsCount = 0 + open var endPollPollStartEventIdTextCallsCount: Int { get { if Thread.isMainThread { - return endPollPollStartIdTextUnderlyingCallsCount + return endPollPollStartEventIdTextUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = endPollPollStartIdTextUnderlyingCallsCount + returnValue = endPollPollStartEventIdTextUnderlyingCallsCount } return returnValue! @@ -19001,31 +17848,31 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - endPollPollStartIdTextUnderlyingCallsCount = newValue + endPollPollStartEventIdTextUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - endPollPollStartIdTextUnderlyingCallsCount = newValue + endPollPollStartEventIdTextUnderlyingCallsCount = newValue } } } } - open var endPollPollStartIdTextCalled: Bool { - return endPollPollStartIdTextCallsCount > 0 + open var endPollPollStartEventIdTextCalled: Bool { + return endPollPollStartEventIdTextCallsCount > 0 } - open var endPollPollStartIdTextReceivedArguments: (pollStartId: String, text: String)? - open var endPollPollStartIdTextReceivedInvocations: [(pollStartId: String, text: String)] = [] - open var endPollPollStartIdTextClosure: ((String, String) throws -> Void)? + open var endPollPollStartEventIdTextReceivedArguments: (pollStartEventId: String, text: String)? + open var endPollPollStartEventIdTextReceivedInvocations: [(pollStartEventId: String, text: String)] = [] + open var endPollPollStartEventIdTextClosure: ((String, String) throws -> Void)? - open override func endPoll(pollStartId: String, text: String) throws { - if let error = endPollPollStartIdTextThrowableError { + open override func endPoll(pollStartEventId: String, text: String) throws { + if let error = endPollPollStartEventIdTextThrowableError { throw error } - endPollPollStartIdTextCallsCount += 1 - endPollPollStartIdTextReceivedArguments = (pollStartId: pollStartId, text: text) + endPollPollStartEventIdTextCallsCount += 1 + endPollPollStartEventIdTextReceivedArguments = (pollStartEventId: pollStartEventId, text: text) DispatchQueue.main.async { - self.endPollPollStartIdTextReceivedInvocations.append((pollStartId: pollStartId, text: text)) + self.endPollPollStartEventIdTextReceivedInvocations.append((pollStartEventId: pollStartEventId, text: text)) } - try endPollPollStartIdTextClosure?(pollStartId, text) + try endPollPollStartEventIdTextClosure?(pollStartEventId, text) } //MARK: - fetchDetailsForEvent @@ -19608,16 +18455,16 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { //MARK: - redactEvent - open var redactEventItemReasonThrowableError: Error? - var redactEventItemReasonUnderlyingCallsCount = 0 - open var redactEventItemReasonCallsCount: Int { + open var redactEventEventOrTransactionIdReasonThrowableError: Error? + var redactEventEventOrTransactionIdReasonUnderlyingCallsCount = 0 + open var redactEventEventOrTransactionIdReasonCallsCount: Int { get { if Thread.isMainThread { - return redactEventItemReasonUnderlyingCallsCount + return redactEventEventOrTransactionIdReasonUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = redactEventItemReasonUnderlyingCallsCount + returnValue = redactEventEventOrTransactionIdReasonUnderlyingCallsCount } return returnValue! @@ -19625,60 +18472,31 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - redactEventItemReasonUnderlyingCallsCount = newValue + redactEventEventOrTransactionIdReasonUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - redactEventItemReasonUnderlyingCallsCount = newValue + redactEventEventOrTransactionIdReasonUnderlyingCallsCount = newValue } } } } - open var redactEventItemReasonCalled: Bool { - return redactEventItemReasonCallsCount > 0 - } - open var redactEventItemReasonReceivedArguments: (item: EventTimelineItem, reason: String?)? - open var redactEventItemReasonReceivedInvocations: [(item: EventTimelineItem, reason: String?)] = [] - - var redactEventItemReasonUnderlyingReturnValue: Bool! - open var redactEventItemReasonReturnValue: Bool! { - get { - if Thread.isMainThread { - return redactEventItemReasonUnderlyingReturnValue - } else { - var returnValue: Bool? = nil - DispatchQueue.main.sync { - returnValue = redactEventItemReasonUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - redactEventItemReasonUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - redactEventItemReasonUnderlyingReturnValue = newValue - } - } - } + open var redactEventEventOrTransactionIdReasonCalled: Bool { + return redactEventEventOrTransactionIdReasonCallsCount > 0 } - open var redactEventItemReasonClosure: ((EventTimelineItem, String?) async throws -> Bool)? + open var redactEventEventOrTransactionIdReasonReceivedArguments: (eventOrTransactionId: EventOrTransactionId, reason: String?)? + open var redactEventEventOrTransactionIdReasonReceivedInvocations: [(eventOrTransactionId: EventOrTransactionId, reason: String?)] = [] + open var redactEventEventOrTransactionIdReasonClosure: ((EventOrTransactionId, String?) async throws -> Void)? - open override func redactEvent(item: EventTimelineItem, reason: String?) async throws -> Bool { - if let error = redactEventItemReasonThrowableError { + open override func redactEvent(eventOrTransactionId: EventOrTransactionId, reason: String?) async throws { + if let error = redactEventEventOrTransactionIdReasonThrowableError { throw error } - redactEventItemReasonCallsCount += 1 - redactEventItemReasonReceivedArguments = (item: item, reason: reason) + redactEventEventOrTransactionIdReasonCallsCount += 1 + redactEventEventOrTransactionIdReasonReceivedArguments = (eventOrTransactionId: eventOrTransactionId, reason: reason) DispatchQueue.main.async { - self.redactEventItemReasonReceivedInvocations.append((item: item, reason: reason)) - } - if let redactEventItemReasonClosure = redactEventItemReasonClosure { - return try await redactEventItemReasonClosure(item, reason) - } else { - return redactEventItemReasonReturnValue + self.redactEventEventOrTransactionIdReasonReceivedInvocations.append((eventOrTransactionId: eventOrTransactionId, reason: reason)) } + try await redactEventEventOrTransactionIdReasonClosure?(eventOrTransactionId, reason) } //MARK: - retryDecryption @@ -20055,16 +18873,16 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { //MARK: - sendPollResponse - open var sendPollResponsePollStartIdAnswersThrowableError: Error? - var sendPollResponsePollStartIdAnswersUnderlyingCallsCount = 0 - open var sendPollResponsePollStartIdAnswersCallsCount: Int { + open var sendPollResponsePollStartEventIdAnswersThrowableError: Error? + var sendPollResponsePollStartEventIdAnswersUnderlyingCallsCount = 0 + open var sendPollResponsePollStartEventIdAnswersCallsCount: Int { get { if Thread.isMainThread { - return sendPollResponsePollStartIdAnswersUnderlyingCallsCount + return sendPollResponsePollStartEventIdAnswersUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = sendPollResponsePollStartIdAnswersUnderlyingCallsCount + returnValue = sendPollResponsePollStartEventIdAnswersUnderlyingCallsCount } return returnValue! @@ -20072,31 +18890,31 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendPollResponsePollStartIdAnswersUnderlyingCallsCount = newValue + sendPollResponsePollStartEventIdAnswersUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - sendPollResponsePollStartIdAnswersUnderlyingCallsCount = newValue + sendPollResponsePollStartEventIdAnswersUnderlyingCallsCount = newValue } } } } - open var sendPollResponsePollStartIdAnswersCalled: Bool { - return sendPollResponsePollStartIdAnswersCallsCount > 0 + open var sendPollResponsePollStartEventIdAnswersCalled: Bool { + return sendPollResponsePollStartEventIdAnswersCallsCount > 0 } - open var sendPollResponsePollStartIdAnswersReceivedArguments: (pollStartId: String, answers: [String])? - open var sendPollResponsePollStartIdAnswersReceivedInvocations: [(pollStartId: String, answers: [String])] = [] - open var sendPollResponsePollStartIdAnswersClosure: ((String, [String]) async throws -> Void)? + open var sendPollResponsePollStartEventIdAnswersReceivedArguments: (pollStartEventId: String, answers: [String])? + open var sendPollResponsePollStartEventIdAnswersReceivedInvocations: [(pollStartEventId: String, answers: [String])] = [] + open var sendPollResponsePollStartEventIdAnswersClosure: ((String, [String]) async throws -> Void)? - open override func sendPollResponse(pollStartId: String, answers: [String]) async throws { - if let error = sendPollResponsePollStartIdAnswersThrowableError { + open override func sendPollResponse(pollStartEventId: String, answers: [String]) async throws { + if let error = sendPollResponsePollStartEventIdAnswersThrowableError { throw error } - sendPollResponsePollStartIdAnswersCallsCount += 1 - sendPollResponsePollStartIdAnswersReceivedArguments = (pollStartId: pollStartId, answers: answers) + sendPollResponsePollStartEventIdAnswersCallsCount += 1 + sendPollResponsePollStartEventIdAnswersReceivedArguments = (pollStartEventId: pollStartEventId, answers: answers) DispatchQueue.main.async { - self.sendPollResponsePollStartIdAnswersReceivedInvocations.append((pollStartId: pollStartId, answers: answers)) + self.sendPollResponsePollStartEventIdAnswersReceivedInvocations.append((pollStartEventId: pollStartEventId, answers: answers)) } - try await sendPollResponsePollStartIdAnswersClosure?(pollStartId, answers) + try await sendPollResponsePollStartEventIdAnswersClosure?(pollStartEventId, answers) } //MARK: - sendReadReceipt @@ -21685,147 +20503,6 @@ open class TimelineItemSDKMock: MatrixRustSDK.TimelineItem { } } } -open class TimelineItemContentSDKMock: MatrixRustSDK.TimelineItemContent { - init() { - super.init(noPointer: .init()) - } - - public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { - fatalError("init(unsafeFromRawPointer:) has not been implemented") - } - - fileprivate var pointer: UnsafeMutableRawPointer! - - //MARK: - asMessage - - var asMessageUnderlyingCallsCount = 0 - open var asMessageCallsCount: Int { - get { - if Thread.isMainThread { - return asMessageUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = asMessageUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - asMessageUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - asMessageUnderlyingCallsCount = newValue - } - } - } - } - open var asMessageCalled: Bool { - return asMessageCallsCount > 0 - } - - var asMessageUnderlyingReturnValue: Message? - open var asMessageReturnValue: Message? { - get { - if Thread.isMainThread { - return asMessageUnderlyingReturnValue - } else { - var returnValue: Message?? = nil - DispatchQueue.main.sync { - returnValue = asMessageUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - asMessageUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - asMessageUnderlyingReturnValue = newValue - } - } - } - } - open var asMessageClosure: (() -> Message?)? - - open override func asMessage() -> Message? { - asMessageCallsCount += 1 - if let asMessageClosure = asMessageClosure { - return asMessageClosure() - } else { - return asMessageReturnValue - } - } - - //MARK: - kind - - var kindUnderlyingCallsCount = 0 - open var kindCallsCount: Int { - get { - if Thread.isMainThread { - return kindUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = kindUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - kindUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - kindUnderlyingCallsCount = newValue - } - } - } - } - open var kindCalled: Bool { - return kindCallsCount > 0 - } - - var kindUnderlyingReturnValue: TimelineItemContentKind! - open var kindReturnValue: TimelineItemContentKind! { - get { - if Thread.isMainThread { - return kindUnderlyingReturnValue - } else { - var returnValue: TimelineItemContentKind? = nil - DispatchQueue.main.sync { - returnValue = kindUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - kindUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - kindUnderlyingReturnValue = newValue - } - } - } - } - open var kindClosure: (() -> TimelineItemContentKind)? - - open override func kind() -> TimelineItemContentKind { - kindCallsCount += 1 - if let kindClosure = kindClosure { - return kindClosure() - } else { - return kindReturnValue - } - } -} open class UnreadNotificationsCountSDKMock: MatrixRustSDK.UnreadNotificationsCount { init() { super.init(noPointer: .init()) diff --git a/ElementX/Sources/Mocks/PollMock.swift b/ElementX/Sources/Mocks/PollMock.swift index cc3322871b..0da15ef331 100644 --- a/ElementX/Sources/Mocks/PollMock.swift +++ b/ElementX/Sources/Mocks/PollMock.swift @@ -82,7 +82,7 @@ extension Poll.Option { extension PollRoomTimelineItem { static func mock(poll: Poll, isOutgoing: Bool = true, isEditable: Bool = false) -> Self { - .init(id: .init(timelineID: UUID().uuidString, eventID: UUID().uuidString), + .init(id: .random, poll: poll, body: "poll", timestamp: "Now", diff --git a/ElementX/Sources/Mocks/RoomTimelineProviderMock.swift b/ElementX/Sources/Mocks/RoomTimelineProviderMock.swift index 0e671fcfe0..12a1e4806c 100644 --- a/ElementX/Sources/Mocks/RoomTimelineProviderMock.swift +++ b/ElementX/Sources/Mocks/RoomTimelineProviderMock.swift @@ -35,7 +35,12 @@ class AutoUpdatingRoomTimelineProviderMock: RoomTimelineProvider { let diff = TimelineDiffSDKMock() diff.changeReturnValue = .append - diff.appendReturnValue = [TimelineItemFixtures.messageTimelineItem] + + let timelineItem = TimelineItemSDKMock() + timelineItem.asEventReturnValue = EventTimelineItem.mockMessage + timelineItem.uniqueIdReturnValue = UUID().uuidString + + diff.appendReturnValue = [timelineItem] await Self.timelineListener?.onUpdate(diff: [diff]) } diff --git a/ElementX/Sources/Mocks/SDK/EventTimelineItemSDKMock.swift b/ElementX/Sources/Mocks/SDK/EventTimelineItemSDKMock.swift deleted file mode 100644 index fbeea26eb8..0000000000 --- a/ElementX/Sources/Mocks/SDK/EventTimelineItemSDKMock.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// 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 EventTimelineItemSDKMockConfiguration { - var eventID: String = UUID().uuidString -} - -extension EventTimelineItemSDKMock { - convenience init(configuration: EventTimelineItemSDKMockConfiguration) { - self.init() - eventIdReturnValue = configuration.eventID - isOwnReturnValue = false - timestampReturnValue = 0 - isEditableReturnValue = false - canBeRepliedToReturnValue = false - senderReturnValue = "" - senderProfileReturnValue = .pending - - let timelineItemContent = TimelineItemContentSDKMock() - timelineItemContent.kindReturnValue = .redactedMessage - contentReturnValue = timelineItemContent - } -} diff --git a/ElementX/Sources/Mocks/TimelineItemMock.swift b/ElementX/Sources/Mocks/TimelineItemMock.swift deleted file mode 100644 index 82138364ea..0000000000 --- a/ElementX/Sources/Mocks/TimelineItemMock.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// Copyright 2024 New Vector Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only -// Please see LICENSE in the repository root for full details. -// - -import Combine -import Foundation -import LoremSwiftum -import MatrixRustSDK - -enum TimelineItemFixtures { - static var callInviteTimelineItem: TimelineItem { - let eventTimelineItem = EventTimelineItemSDKMock() - eventTimelineItem.isOwnReturnValue = true - eventTimelineItem.timestampReturnValue = 0 - eventTimelineItem.isEditableReturnValue = false - eventTimelineItem.canBeRepliedToReturnValue = false - eventTimelineItem.senderReturnValue = "@bob:matrix.org" - eventTimelineItem.senderProfileReturnValue = .pending - - let timelineItemContent = TimelineItemContentSDKMock() - timelineItemContent.kindReturnValue = .callInvite - eventTimelineItem.contentReturnValue = timelineItemContent - - let timelineItem = TimelineItemSDKMock() - timelineItem.asEventReturnValue = eventTimelineItem - - return timelineItem - } - - static var messageTimelineItem: TimelineItem { - let eventTimelineItem = EventTimelineItemSDKMock() - eventTimelineItem.eventIdReturnValue = UUID().uuidString - eventTimelineItem.isOwnReturnValue = true - eventTimelineItem.timestampReturnValue = 0 - eventTimelineItem.isEditableReturnValue = false - eventTimelineItem.canBeRepliedToReturnValue = false - eventTimelineItem.senderReturnValue = "@bob:matrix.org" - eventTimelineItem.senderProfileReturnValue = .pending - eventTimelineItem.reactionsReturnValue = [] - eventTimelineItem.readReceiptsReturnValue = [:] - - let timelineItemContent = TimelineItemContentSDKMock() - - timelineItemContent.kindReturnValue = .message - - let message = MessageSDKMock() - - let textMessageContent = TextMessageContent(body: Lorem.sentences(Int.random(in: 1...5)), formatted: nil) - message.msgtypeReturnValue = .text(content: textMessageContent) - message.isThreadedReturnValue = false - message.isEditedReturnValue = false - - timelineItemContent.asMessageReturnValue = message - - eventTimelineItem.contentReturnValue = timelineItemContent - - let timelineItem = TimelineItemSDKMock() - timelineItem.asEventReturnValue = eventTimelineItem - timelineItem.uniqueIdReturnValue = UUID().uuidString - - return timelineItem - } -} diff --git a/ElementX/Sources/Other/Extensions/Array.swift b/ElementX/Sources/Other/Extensions/Array.swift index 7622916e85..a5fd0fd0a7 100644 --- a/ElementX/Sources/Other/Extensions/Array.swift +++ b/ElementX/Sources/Other/Extensions/Array.swift @@ -61,6 +61,6 @@ extension Array { extension Array where Element == RoomTimelineItemProtocol { func firstUsingStableID(_ id: TimelineItemIdentifier) -> Element? { - first { $0.id.timelineID == id.timelineID } + first { $0.id.uniqueID == id.uniqueID } } } diff --git a/ElementX/Sources/Other/Extensions/ProposedViewSize.swift b/ElementX/Sources/Other/Extensions/ProposedViewSize.swift index 17067d58c6..a9995015ea 100644 --- a/ElementX/Sources/Other/Extensions/ProposedViewSize.swift +++ b/ElementX/Sources/Other/Extensions/ProposedViewSize.swift @@ -7,7 +7,7 @@ import SwiftUI -extension ProposedViewSize: Hashable { +extension ProposedViewSize: @retroactive Hashable { public func hash(into hasher: inout Hasher) { hasher.combine(width) hasher.combine(height) diff --git a/ElementX/Sources/Other/Extensions/URL.swift b/ElementX/Sources/Other/Extensions/URL.swift index e23d72aea5..3506d46c5f 100644 --- a/ElementX/Sources/Other/Extensions/URL.swift +++ b/ElementX/Sources/Other/Extensions/URL.swift @@ -7,7 +7,7 @@ import Foundation -extension URL: ExpressibleByStringLiteral { +extension URL: @retroactive ExpressibleByStringLiteral { public init(stringLiteral value: StaticString) { guard let url = URL(string: "\(value)") else { fatalError("The static string used to create this URL is invalid") diff --git a/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift b/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift index cb1fec8ea1..5975bfabab 100644 --- a/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift +++ b/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift @@ -93,7 +93,7 @@ private struct MessageForwardingListRow: View { struct MessageForwardingScreen_Previews: PreviewProvider, TestablePreview { static var previews: some View { let summaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))) - let viewModel = MessageForwardingScreenViewModel(forwardingItem: .init(id: .init(timelineID: ""), + let viewModel = MessageForwardingScreenViewModel(forwardingItem: .init(id: .init(uniqueID: ""), roomID: "", content: .init(noPointer: .init())), clientProxy: ClientProxyMock(.init()), diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift index f9bad3c7de..970ee05be9 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift @@ -258,9 +258,9 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool case .newMessage: set(mode: .default) case .edit(let eventID): - set(mode: .edit(originalItemId: .init(timelineID: "", eventID: eventID))) + set(mode: .edit(originalItemId: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: eventID)))) case .reply(let eventID): - set(mode: .reply(itemID: .init(timelineID: "", eventID: eventID), replyDetails: .loading(eventID: eventID), isThread: false)) + set(mode: .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: eventID)), replyDetails: .loading(eventID: eventID), isThread: false)) replyLoadingTask = Task { let reply = switch await draftService.getReply(eventID: eventID) { case .success(let reply): @@ -273,7 +273,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool return } - set(mode: .reply(itemID: .init(timelineID: "", eventID: eventID), replyDetails: reply.details, isThread: reply.isThreaded)) + set(mode: .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: eventID)), replyDetails: reply.details, isThread: reply.isThreaded)) } } } diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift index bf61858663..0c463ad1e2 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift @@ -415,10 +415,10 @@ extension ComposerToolbar { mentionDisplayHelper: ComposerMentionDisplayHelper.mock, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) - model.state.composerMode = isLoading ? .reply(itemID: .init(timelineID: ""), + model.state.composerMode = isLoading ? .reply(itemID: .init(uniqueID: ""), replyDetails: .loading(eventID: ""), isThread: false) : - .reply(itemID: .init(timelineID: ""), + .reply(itemID: .init(uniqueID: ""), replyDetails: .loaded(sender: .init(id: "", displayName: "Test"), eventID: "", eventContent: .message(.text(.init(body: "Hello World!")))), isThread: false) diff --git a/ElementX/Sources/Screens/Timeline/TimelineModels.swift b/ElementX/Sources/Screens/Timeline/TimelineModels.swift index fc64307211..ab72290152 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineModels.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineModels.swift @@ -203,7 +203,7 @@ struct TimelineState { var itemsDictionary = OrderedDictionary() - var timelineIDs: [String] { + var uniqueIDs: [String] { itemsDictionary.keys.elements } diff --git a/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift b/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift index b85fbb3fdb..6b32e9c74c 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift @@ -401,8 +401,8 @@ class TimelineTableViewController: UIViewController { // These are already in reverse order because the table view is flipped for indexPath in visibleIndexPaths { - if let visibleItemTimelineID = dataSource?.itemIdentifier(for: indexPath), - let visibleItemID = timelineItemsDictionary[visibleItemTimelineID]?.identifier { + if let visibleItemUniqueID = dataSource?.itemIdentifier(for: indexPath), + let visibleItemID = timelineItemsDictionary[visibleItemUniqueID]?.identifier { coordinator.send(viewAction: .sendReadReceiptIfNeeded(visibleItemID)) return } @@ -488,7 +488,7 @@ extension TimelineTableViewController { /// The current layout of the table, based on the newest timeline item. private func snapshotLayout() -> Layout? { guard let newestItemID = newestVisibleItemID(), - let newestCellFrame = cellFrame(for: newestItemID.timelineID) else { + let newestCellFrame = cellFrame(for: newestItemID.uniqueID) else { return nil } return Layout(id: newestItemID, frame: newestCellFrame) @@ -496,12 +496,12 @@ extension TimelineTableViewController { /// Restores the timeline's layout from an old snapshot. private func restoreLayout(_ layout: Layout) { - if let indexPath = dataSource?.indexPath(for: layout.id.timelineID) { + if let indexPath = dataSource?.indexPath(for: layout.id.uniqueID) { // Scroll the item into view. tableView.scrollToRow(at: indexPath, at: .top, animated: false) // Remove any unwanted offset that was added by scrollToRow. - if let frame = cellFrame(for: layout.id.timelineID) { + if let frame = cellFrame(for: layout.id.uniqueID) { let deltaY = frame.maxY - layout.frame.maxY if deltaY != 0 { tableView.contentOffset.y -= deltaY @@ -511,8 +511,8 @@ extension TimelineTableViewController { } /// Returns the frame of the cell for a particular timeline item. - private func cellFrame(for id: String) -> CGRect? { - guard let timelineCell = tableView.visibleCells.first(where: { ($0 as? TimelineItemCell)?.item?.id == id }) else { + private func cellFrame(for uniqueID: String) -> CGRect? { + guard let timelineCell = tableView.visibleCells.first(where: { ($0 as? TimelineItemCell)?.item?.identifier.uniqueID == uniqueID }) else { return nil } diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index ffc0386f20..e6d37a7dfa 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -659,19 +659,19 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { if itemGroup.count == 1 { if let firstItem = itemGroup.first { timelineItemsDictionary.updateValue(updateViewState(item: firstItem, groupStyle: .single), - forKey: firstItem.id.timelineID) + forKey: firstItem.id.uniqueID) } } else { for (index, item) in itemGroup.enumerated() { if index == 0 { timelineItemsDictionary.updateValue(updateViewState(item: item, groupStyle: state.isPinnedEventsTimeline ? .single : .first), - forKey: item.id.timelineID) + forKey: item.id.uniqueID) } else if index == itemGroup.count - 1 { timelineItemsDictionary.updateValue(updateViewState(item: item, groupStyle: state.isPinnedEventsTimeline ? .single : .last), - forKey: item.id.timelineID) + forKey: item.id.uniqueID) } else { timelineItemsDictionary.updateValue(updateViewState(item: item, groupStyle: state.isPinnedEventsTimeline ? .single : .middle), - forKey: item.id.timelineID) + forKey: item.id.uniqueID) } } } @@ -685,7 +685,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { } private func updateViewState(item: RoomTimelineItemProtocol, groupStyle: TimelineGroupStyle) -> RoomTimelineItemViewState { - if let timelineItemViewState = state.timelineViewState.itemsDictionary[item.id.timelineID] { + if let timelineItemViewState = state.timelineViewState.itemsDictionary[item.id.uniqueID] { timelineItemViewState.groupStyle = groupStyle timelineItemViewState.type = .init(item: item) return timelineItemViewState diff --git a/ElementX/Sources/Screens/Timeline/View/Style/SwipeToReplyView.swift b/ElementX/Sources/Screens/Timeline/View/Style/SwipeToReplyView.swift index fb4520621e..47b611a66b 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/SwipeToReplyView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/SwipeToReplyView.swift @@ -19,7 +19,7 @@ struct SwipeToReplyView: View { } struct SwipeToReplyView_Previews: PreviewProvider, TestablePreview { - static let timelineItem = TextRoomTimelineItem(id: .init(timelineID: ""), + static let timelineItem = TextRoomTimelineItem(id: .init(uniqueID: ""), timestamp: "", isOutgoing: true, isEditable: true, diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift index 49ca3828bd..894d9ef529 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift @@ -388,7 +388,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview // These always include a reply static var threads: some View { ScrollView { - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -401,7 +401,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview eventContent: .message(.text(.init(body: "Short"))))), groupStyle: .single)) - AudioRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""), + AudioRoomTimelineView(timelineItem: .init(id: .init(uniqueID: ""), timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -417,7 +417,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview eventID: "123", eventContent: .message(.text(.init(body: "Short")))))) - FileRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""), + FileRoomTimelineView(timelineItem: .init(id: .init(uniqueID: ""), timestamp: "10:42", isOutgoing: false, isEditable: false, @@ -431,7 +431,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"), eventID: "123", eventContent: .message(.text(.init(body: "Short")))))) - ImageRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""), + ImageRoomTimelineView(timelineItem: .init(id: .init(uniqueID: ""), timestamp: "10:42", isOutgoing: true, isEditable: true, @@ -469,7 +469,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview eventID: "123", eventContent: .message(.text(.init(body: "Short")))))) - VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""), + VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(uniqueID: ""), timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -505,7 +505,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview static var replies: some View { VStack(spacing: 0) { - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -518,7 +518,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview eventContent: .message(.text(.init(body: "Short"))))), groupStyle: .single)) - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -536,7 +536,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview static var encryptionAuthenticity: some View { VStack(spacing: 0) { - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -547,7 +547,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview properties: RoomTimelineItemProperties(encryptionAuthenticity: .unsignedDevice(color: .red))), groupStyle: .single)) - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -559,7 +559,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview encryptionAuthenticity: .unsignedDevice(color: .red))), groupStyle: .single)) - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), timestamp: "10:42", isOutgoing: false, isEditable: false, @@ -570,7 +570,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview properties: RoomTimelineItemProperties(encryptionAuthenticity: .unknownDevice(color: .red))), groupStyle: .first)) - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), timestamp: "10:42", isOutgoing: false, isEditable: false, @@ -592,7 +592,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview properties: RoomTimelineItemProperties(encryptionAuthenticity: .notGuaranteed(color: .gray)))) - VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""), + VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(uniqueID: ""), timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -615,7 +615,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview static var pinned: some View { ScrollView { - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: "", eventID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")), timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -626,7 +626,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview replyDetails: nil), groupStyle: .single)) - AudioRoomTimelineView(timelineItem: .init(id: .init(timelineID: "", eventID: ""), + AudioRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")), timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -640,7 +640,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview contentType: nil), replyDetails: nil)) - FileRoomTimelineView(timelineItem: .init(id: .init(timelineID: "", eventID: ""), + FileRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")), timestamp: "10:42", isOutgoing: false, isEditable: false, @@ -652,7 +652,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview thumbnailSource: nil, contentType: nil), replyDetails: nil)) - ImageRoomTimelineView(timelineItem: .init(id: .init(timelineID: "", eventID: ""), + ImageRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")), timestamp: "10:42", isOutgoing: true, isEditable: true, @@ -661,7 +661,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview sender: .init(id: ""), content: .init(body: "Some image", source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), thumbnailSource: nil), replyDetails: nil)) - LocationRoomTimelineView(timelineItem: .init(id: .init(timelineID: "", eventID: ""), + LocationRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")), timestamp: "Now", isOutgoing: false, isEditable: false, @@ -673,7 +673,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview longitude: 12.496366), description: "Location description description description description description description description description"), replyDetails: nil)) - LocationRoomTimelineView(timelineItem: .init(id: .init(timelineID: "", eventID: ""), + LocationRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")), timestamp: "Now", isOutgoing: false, isEditable: false, @@ -684,7 +684,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview geoURI: .init(latitude: 41.902782, longitude: 12.496366), description: nil), replyDetails: nil)) - VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(timelineID: "", eventID: ""), + VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")), timestamp: "10:42", isOutgoing: true, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift index 4d09e9216b..ac7dc5d593 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift @@ -79,8 +79,8 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { }() static let sendingLast: TextRoomTimelineItem = { - let id = viewModel.state.timelineViewState.timelineIDs.last ?? UUID().uuidString - var result = TextRoomTimelineItem(id: .init(timelineID: id), + let id = viewModel.state.timelineViewState.uniqueIDs.last ?? UUID().uuidString + var result = TextRoomTimelineItem(id: .init(uniqueID: id), timestamp: "Now", isOutgoing: true, isEditable: false, @@ -99,8 +99,8 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { }() static let sentLast: TextRoomTimelineItem = { - let id = viewModel.state.timelineViewState.timelineIDs.last ?? UUID().uuidString - let result = TextRoomTimelineItem(id: .init(timelineID: id), + let id = viewModel.state.timelineViewState.uniqueIDs.last ?? UUID().uuidString + let result = TextRoomTimelineItem(id: .init(uniqueID: id), timestamp: "Now", isOutgoing: true, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineItemStatusView.swift b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineItemStatusView.swift index 071351cc50..1729d149d6 100644 --- a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineItemStatusView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineItemStatusView.swift @@ -14,7 +14,7 @@ struct TimelineItemStatusView: View { @EnvironmentObject private var context: TimelineViewModel.Context private var isLastOutgoingMessage: Bool { - timelineItem.isOutgoing && context.viewState.timelineViewState.timelineIDs.last == timelineItem.id.timelineID + timelineItem.isOutgoing && context.viewState.timelineViewState.uniqueIDs.last == timelineItem.id.uniqueID } var body: some View { diff --git a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReactionsView.swift b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReactionsView.swift index 50b2459e15..f8656fdf23 100644 --- a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReactionsView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReactionsView.swift @@ -196,20 +196,20 @@ struct TimelineReactionViewPreviewsContainer: View { var body: some View { VStack(spacing: 8) { TimelineReactionsView(context: TimelineViewModel.mock.context, - itemID: .init(timelineID: "1"), + itemID: .init(uniqueID: "1"), reactions: [AggregatedReaction.mockReactionWithLongText, AggregatedReaction.mockReactionWithLongTextRTL]) Divider() TimelineReactionsView(context: TimelineViewModel.mock.context, - itemID: .init(timelineID: "2"), + itemID: .init(uniqueID: "2"), reactions: Array(AggregatedReaction.mockReactions.prefix(3))) Divider() TimelineReactionsView(context: TimelineViewModel.mock.context, - itemID: .init(timelineID: "3"), + itemID: .init(uniqueID: "3"), reactions: AggregatedReaction.mockReactions) Divider() TimelineReactionsView(context: TimelineViewModel.mock.context, - itemID: .init(timelineID: "4"), + itemID: .init(uniqueID: "4"), reactions: AggregatedReaction.mockReactions, isLayoutRTL: true) } diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift index 9e03a81794..fd50654fe1 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift @@ -52,8 +52,8 @@ struct CollapsibleRoomTimelineView: View { struct CollapsibleRoomTimelineView_Previews: PreviewProvider, TestablePreview { static let item = CollapsibleTimelineItem(items: [ - SeparatorRoomTimelineItem(id: .init(timelineID: "First separator"), text: "This is a separator"), - SeparatorRoomTimelineItem(id: .init(timelineID: "Second separator"), text: "This is another separator") + SeparatorRoomTimelineItem(id: .init(uniqueID: "First separator"), text: "This is a separator"), + SeparatorRoomTimelineItem(id: .init(uniqueID: "Second separator"), text: "This is another separator") ]) static var previews: some View { diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift index 5c6f5c58bf..71a3b1a0f3 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift @@ -29,12 +29,12 @@ struct ReadMarkerRoomTimelineView: View { struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview { static let viewModel = TimelineViewModel.mock - static let item = ReadMarkerRoomTimelineItem(id: .init(timelineID: .init(UUID().uuidString))) + static let item = ReadMarkerRoomTimelineItem(id: .init(uniqueID: .init(UUID().uuidString))) static var previews: some View { VStack(alignment: .leading, spacing: 0) { - RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .init(timelineID: "Separator"), text: "Today")), groupStyle: .single)) - RoomTimelineItemView(viewState: .init(type: .text(.init(id: .init(timelineID: ""), + RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .init(uniqueID: "Separator"), text: "Today")), groupStyle: .single)) + RoomTimelineItemView(viewState: .init(type: .text(.init(id: .random, timestamp: "", isOutgoing: true, isEditable: false, @@ -45,8 +45,8 @@ struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview { ReadMarkerRoomTimelineView(timelineItem: item) - RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .init(timelineID: "Separator"), text: "Today")), groupStyle: .single)) - RoomTimelineItemView(viewState: .init(type: .text(.init(id: .init(timelineID: ""), + RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .init(uniqueID: "Separator"), text: "Today")), groupStyle: .single)) + RoomTimelineItemView(viewState: .init(type: .text(.init(id: .random, timestamp: "", isOutgoing: false, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift index 0f74079076..13c5068149 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift @@ -23,7 +23,7 @@ struct SeparatorRoomTimelineView: View { struct SeparatorRoomTimelineView_Previews: PreviewProvider, TestablePreview { static var previews: some View { - let item = SeparatorRoomTimelineItem(id: .init(timelineID: "Separator"), text: "This is a separator") + let item = SeparatorRoomTimelineItem(id: .init(uniqueID: "Separator"), text: "This is a separator") SeparatorRoomTimelineView(timelineItem: item) } } diff --git a/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift b/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift index e762e27cb8..0159b7502a 100644 --- a/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift +++ b/ElementX/Sources/Services/Authentication/AuthenticationServiceProtocol.swift @@ -79,7 +79,7 @@ struct OIDCAuthorizationDataProxy: Equatable { } } -extension OidcAuthorizationData: Equatable { +extension OidcAuthorizationData: @retroactive Equatable { public static func == (lhs: MatrixRustSDK.OidcAuthorizationData, rhs: MatrixRustSDK.OidcAuthorizationData) -> Bool { lhs.loginUrl() == rhs.loginUrl() } diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomEventStringBuilder.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomEventStringBuilder.swift index beed3f206a..55a05efd2a 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomEventStringBuilder.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomEventStringBuilder.swift @@ -23,7 +23,7 @@ struct RoomEventStringBuilder { sender.displayName ?? sender.id } - switch eventItemProxy.content.kind() { + switch eventItemProxy.content { case .unableToDecrypt(let encryptedMessage): let errorMessage = switch encryptedMessage { case .megolmV1AesSha2(_, .membership): L10n.commonUnableToDecryptNoAccess @@ -41,13 +41,8 @@ struct RoomEventStringBuilder { return prefix(L10n.commonSticker, with: displayName) case .failedToParseMessageLike, .failedToParseState: return prefix(L10n.commonUnsupportedEvent, with: displayName) - case .message: - guard let messageContent = eventItemProxy.content.asMessage() else { - fatalError("Invalid message timeline item: \(eventItemProxy)") - } - - let messageType = messageContent.msgtype() - return messageEventStringBuilder.buildAttributedString(for: messageType, senderDisplayName: displayName) + case .message(let messageContent): + return messageEventStringBuilder.buildAttributedString(for: messageContent.msgType, senderDisplayName: displayName) case .state(_, let state): return stateEventStringBuilder .buildString(for: state, sender: sender, isOutgoing: isOutgoing) diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift index 0d9921a707..7c66bbc28a 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift @@ -246,7 +246,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { var lastMessageFormattedTimestamp: String? if let latestRoomMessage = roomDetails.latestEvent { - let lastMessage = EventTimelineItemProxy(item: latestRoomMessage, id: "0") + let lastMessage = EventTimelineItemProxy(item: latestRoomMessage, uniqueID: "0") lastMessageFormattedTimestamp = lastMessage.timestamp.formattedMinimal() attributedLastMessage = eventStringBuilder.buildAttributedString(for: lastMessage) } diff --git a/ElementX/Sources/Services/SecureBackup/SecureBackupController.swift b/ElementX/Sources/Services/SecureBackup/SecureBackupController.swift index fc6c81256f..ab514800de 100644 --- a/ElementX/Sources/Services/SecureBackup/SecureBackupController.swift +++ b/ElementX/Sources/Services/SecureBackup/SecureBackupController.swift @@ -121,7 +121,7 @@ class SecureBackupController: SecureBackupControllerProtocol { MXLog.info("Enabling recovery") var keyUploadErrored = false - let recoveryKey = try await encryption.enableRecovery(waitForBackupsToUpload: false, progressListener: SecureBackupEnableRecoveryProgressListener { [weak self] state in + let recoveryKey = try await encryption.enableRecovery(waitForBackupsToUpload: false, passphrase: nil, progressListener: SecureBackupEnableRecoveryProgressListener { [weak self] state in guard let self else { return } switch state { diff --git a/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift b/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift index c994d8a82e..62cad3cd3f 100644 --- a/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift +++ b/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift @@ -10,9 +10,9 @@ import Foundation enum RoomTimelineItemFixtures { /// The default timeline items used in Xcode previews etc. static var `default`: [RoomTimelineItemProtocol] = [ - SeparatorRoomTimelineItem(id: .init(timelineID: "Yesterday"), text: "Yesterday"), - TextRoomTimelineItem(id: .init(timelineID: ".RoomTimelineItemFixtures.default.0", - eventID: "RoomTimelineItemFixtures.default.0"), + SeparatorRoomTimelineItem(id: .init(uniqueID: "Yesterday"), text: "Yesterday"), + TextRoomTimelineItem(id: .init(uniqueID: ".RoomTimelineItemFixtures.default.0", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.0")), timestamp: "10:10 AM", isOutgoing: false, isEditable: false, @@ -21,8 +21,8 @@ enum RoomTimelineItemFixtures { sender: .init(id: "", displayName: "Jacob"), content: .init(body: "That looks so good!"), properties: RoomTimelineItemProperties(isEdited: true)), - TextRoomTimelineItem(id: .init(timelineID: "RoomTimelineItemFixtures.default.1", - eventID: "RoomTimelineItemFixtures.default.1"), + TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.1", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.1")), timestamp: "10:11 AM", isOutgoing: false, isEditable: false, @@ -33,8 +33,8 @@ enum RoomTimelineItemFixtures { properties: RoomTimelineItemProperties(reactions: [ AggregatedReaction(accountOwnerID: "me", key: "🙌", senders: [ReactionSender(id: "me", timestamp: Date())]) ])), - TextRoomTimelineItem(id: .init(timelineID: "RoomTimelineItemFixtures.default.2", - eventID: "RoomTimelineItemFixtures.default.2"), + TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.2", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.2")), timestamp: "10:11 AM", isOutgoing: false, isEditable: false, @@ -52,9 +52,9 @@ enum RoomTimelineItemFixtures { ReactionSender(id: "jacob", timestamp: Date()) ]) ])), - SeparatorRoomTimelineItem(id: .init(timelineID: "Today"), text: "Today"), - TextRoomTimelineItem(id: .init(timelineID: "RoomTimelineItemFixtures.default.3", - eventID: "RoomTimelineItemFixtures.default.3"), + SeparatorRoomTimelineItem(id: .init(uniqueID: "Today"), text: "Today"), + TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.3", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.3")), timestamp: "5 PM", isOutgoing: false, isEditable: false, @@ -63,8 +63,8 @@ enum RoomTimelineItemFixtures { sender: .init(id: "", displayName: "Helena"), content: .init(body: "Wow, cool. Ok, lets go the usual place tomorrow?! Is that too soon? Here’s the menu, let me know what you want it’s on me!"), properties: RoomTimelineItemProperties(orderedReadReceipts: [ReadReceipt(userID: "alice", formattedTimestamp: nil)])), - TextRoomTimelineItem(id: .init(timelineID: "RoomTimelineItemFixtures.default.4", - eventID: "RoomTimelineItemFixtures.default.4"), + TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.4", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.4")), timestamp: "5 PM", isOutgoing: true, isEditable: true, @@ -72,8 +72,8 @@ enum RoomTimelineItemFixtures { isThreaded: false, sender: .init(id: "", displayName: "Bob"), content: .init(body: "And John's speech was amazing!")), - TextRoomTimelineItem(id: .init(timelineID: "RoomTimelineItemFixtures.default.5", - eventID: "RoomTimelineItemFixtures.default.5"), + TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.5", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.5")), timestamp: "5 PM", isOutgoing: true, isEditable: true, @@ -86,8 +86,8 @@ enum RoomTimelineItemFixtures { ReadReceipt(userID: "bob", formattedTimestamp: nil), ReadReceipt(userID: "charlie", formattedTimestamp: nil), ReadReceipt(userID: "dan", formattedTimestamp: nil)])), - TextRoomTimelineItem(id: .init(timelineID: "RoomTimelineItemFixtures.default.6", - eventID: "RoomTimelineItemFixtures.default.6"), + TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.6", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.6")), timestamp: "5 PM", isOutgoing: false, isEditable: false, @@ -242,7 +242,7 @@ enum RoomTimelineItemFixtures { static var permalinkChunk: [RoomTimelineItemProtocol] { (1...20).map { index in - TextRoomTimelineItem(id: .init(timelineID: "\(index)", eventID: "$\(index)"), + TextRoomTimelineItem(id: .init(uniqueID: "\(index)", eventOrTransactionID: .eventId(eventId: "$\(index)")), text: "Message ID \(index)", senderDisplayName: index > 10 ? "Alice" : "Bob") } diff --git a/ElementX/Sources/Services/Timeline/RoomTimelineProvider.swift b/ElementX/Sources/Services/Timeline/RoomTimelineProvider.swift index ce7c96fa33..e446a40ee5 100644 --- a/ElementX/Sources/Services/Timeline/RoomTimelineProvider.swift +++ b/ElementX/Sources/Services/Timeline/RoomTimelineProvider.swift @@ -184,38 +184,11 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol { } } -private extension TimelineItem { - var debugIdentifier: DebugIdentifier { - if let virtualTimelineItem = asVirtual() { - return .virtual(timelineID: String(uniqueId()), dscription: virtualTimelineItem.description) - } else if let eventTimelineItem = asEvent() { - return .event(timelineID: String(uniqueId()), - eventID: eventTimelineItem.eventId(), - transactionID: eventTimelineItem.transactionId()) - } - - return .unknown(timelineID: String(uniqueId())) - } -} - private extension TimelineItemProxy { - var debugIdentifier: DebugIdentifier { - switch self { - case .event(let eventTimelineItem): - return .event(timelineID: eventTimelineItem.id.timelineID, - eventID: eventTimelineItem.id.eventID, - transactionID: eventTimelineItem.id.transactionID) - case .virtual(let virtualTimelineItem, let timelineID): - return .virtual(timelineID: timelineID, dscription: virtualTimelineItem.description) - case .unknown(let item): - return .unknown(timelineID: String(item.uniqueId())) - } - } - var isMembershipChange: Bool { switch self { case .event(let eventTimelineItemProxy): - switch eventTimelineItemProxy.content.kind() { + switch eventTimelineItemProxy.content { case .roomMembership: true default: @@ -238,12 +211,6 @@ private extension VirtualTimelineItem { } } -enum DebugIdentifier { - case event(timelineID: String, eventID: String?, transactionID: String?) - case virtual(timelineID: String, dscription: String) - case unknown(timelineID: String) -} - private final class RoomTimelineListener: TimelineListener { private let onUpdateClosure: ([TimelineDiff]) -> Void diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift index 457020d50d..cf0381f3a6 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift @@ -186,7 +186,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol { MXLog.info("Editing timeline item: \(timelineItemID)") let editMode: EditMode - if !timelineItemID.timelineID.isEmpty, + if !timelineItemID.uniqueID.isEmpty, let timelineItem = liveTimelineProvider.itemProxies.firstEventTimelineItemUsingStableID(timelineItemID) { editMode = .byEvent(timelineItem) } else if let eventID = timelineItemID.eventID { @@ -392,15 +392,15 @@ class RoomTimelineController: RoomTimelineControllerProtocol { } return timelineItem - case .virtual(let virtualItem, let timelineID): + case .virtual(let virtualItem, let uniqueID): switch virtualItem { case .dayDivider(let timestamp): let date = Date(timeIntervalSince1970: TimeInterval(timestamp / 1000)) let dateString = date.formatted(date: .complete, time: .omitted) - return SeparatorRoomTimelineItem(id: .init(timelineID: dateString), text: dateString) + return SeparatorRoomTimelineItem(id: .init(uniqueID: dateString), text: dateString) case .readMarker: - return ReadMarkerRoomTimelineItem(id: .init(timelineID: timelineID)) + return ReadMarkerRoomTimelineItem(id: .init(uniqueID: uniqueID)) } case .unknown: return nil @@ -409,7 +409,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol { private func isItemCollapsible(_ item: TimelineItemProxy) -> Bool { if case let .event(eventItem) = item { - switch eventItem.content.kind() { + switch eventItem.content { case .profileChange, .roomMembership, .state: return true default: diff --git a/ElementX/Sources/Services/Timeline/TimelineItemContent/CustomStringConvertible.swift b/ElementX/Sources/Services/Timeline/TimelineItemContent/CustomStringConvertible.swift index 5017baaa57..bb4799a110 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItemContent/CustomStringConvertible.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItemContent/CustomStringConvertible.swift @@ -9,43 +9,43 @@ import MatrixRustSDK // MARK: Redact message content from logs -extension EmoteMessageContent: CustomStringConvertible { +extension EmoteMessageContent: @retroactive CustomStringConvertible { public var description: String { String(describing: Self.self) } } -extension FileMessageContent: CustomStringConvertible { +extension FileMessageContent: @retroactive CustomStringConvertible { public var description: String { String(describing: Self.self) } } -extension ImageMessageContent: CustomStringConvertible { +extension ImageMessageContent: @retroactive CustomStringConvertible { public var description: String { String(describing: Self.self) } } -extension NoticeMessageContent: CustomStringConvertible { +extension NoticeMessageContent: @retroactive CustomStringConvertible { public var description: String { String(describing: Self.self) } } -extension TextMessageContent: CustomStringConvertible { +extension TextMessageContent: @retroactive CustomStringConvertible { public var description: String { String(describing: Self.self) } } -extension VideoMessageContent: CustomStringConvertible { +extension VideoMessageContent: @retroactive CustomStringConvertible { public var description: String { String(describing: Self.self) } } -extension AudioMessageContent: CustomStringConvertible { +extension AudioMessageContent: @retroactive CustomStringConvertible { public var description: String { String(describing: Self.self) } diff --git a/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift b/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift index 6a94a3614b..b775a8865c 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift @@ -11,35 +11,48 @@ import MatrixRustSDK struct TimelineItemIdentifier: Hashable { /// Stable id across state changes of the timeline item, it uniquely identifies an item in a timeline. /// It's value is consistent only per timeline instance, it should **not** be used to identify an item across timeline instances. - let timelineID: String - - /// Uniquely identifies the timeline item from the server side. - /// Only available for EventTimelineItem and only when the item is returned by the server. - var eventID: String? - - /// Uniquely identifies the local echo of the timeline item. - /// Only available for sent EventTimelineItem that have not been returned by the server yet. - var transactionID: String? + let uniqueID: String + + /// Contains the 2 possible identifiers of an event, either it has a remote event id or a local transaction id, never both or none. + var eventOrTransactionID: EventOrTransactionId? + + var eventID: String? { + switch eventOrTransactionID { + case .eventId(let eventId): + return eventId + default: + return nil + } + } + + var transactionID: String? { + switch eventOrTransactionID { + case .transactionId(let transactionId): + return transactionId + default: + return nil + } + } } extension TimelineItemIdentifier { /// Use only for mocks/tests static var random: Self { - .init(timelineID: UUID().uuidString, eventID: UUID().uuidString) + .init(uniqueID: UUID().uuidString, eventOrTransactionID: .eventId(eventId: UUID().uuidString)) } } /// A light wrapper around timeline items returned from Rust. enum TimelineItemProxy { case event(EventTimelineItemProxy) - case virtual(MatrixRustSDK.VirtualTimelineItem, timelineID: String) + case virtual(MatrixRustSDK.VirtualTimelineItem, uniqueID: String) case unknown(MatrixRustSDK.TimelineItem) init(item: MatrixRustSDK.TimelineItem) { if let eventItem = item.asEvent() { - self = .event(EventTimelineItemProxy(item: eventItem, id: String(item.uniqueId()))) + self = .event(EventTimelineItemProxy(item: eventItem, uniqueID: String(item.uniqueId()))) } else if let virtualItem = item.asVirtual() { - self = .virtual(virtualItem, timelineID: String(item.uniqueId())) + self = .virtual(virtualItem, uniqueID: String(item.uniqueId())) } else { self = .unknown(item) } @@ -92,13 +105,14 @@ class EventTimelineItemProxy { let item: MatrixRustSDK.EventTimelineItem let id: TimelineItemIdentifier - init(item: MatrixRustSDK.EventTimelineItem, id: String) { + init(item: MatrixRustSDK.EventTimelineItem, uniqueID: String) { self.item = item - self.id = TimelineItemIdentifier(timelineID: id, eventID: item.eventId(), transactionID: item.transactionId()) + + id = TimelineItemIdentifier(uniqueID: uniqueID, eventOrTransactionID: item.eventOrTransactionId) } lazy var deliveryStatus: TimelineItemDeliveryStatus? = { - guard let localSendState = item.localSendState() else { + guard let localSendState = item.localSendState else { return nil } @@ -118,43 +132,43 @@ class EventTimelineItemProxy { } }() - lazy var canBeRepliedTo = item.canBeRepliedTo() + lazy var canBeRepliedTo = item.canBeRepliedTo - lazy var content = item.content() + lazy var content = item.content - lazy var isOwn = item.isOwn() + lazy var isOwn = item.isOwn - lazy var isEditable = item.isEditable() + lazy var isEditable = item.isEditable lazy var sender: TimelineItemSender = { - let profile = item.senderProfile() + let profile = item.senderProfile switch profile { case let .ready(displayName, isDisplayNameAmbiguous, avatarUrl): - return .init(id: item.sender(), + return .init(id: item.sender, displayName: displayName, isDisplayNameAmbiguous: isDisplayNameAmbiguous, avatarURL: avatarUrl.flatMap(URL.init(string:))) default: - return .init(id: item.sender(), + return .init(id: item.sender, displayName: nil, isDisplayNameAmbiguous: false, avatarURL: nil) } }() - lazy var reactions = item.reactions() + lazy var reactions = item.reactions - lazy var timestamp = Date(timeIntervalSince1970: TimeInterval(item.timestamp() / 1000)) + lazy var timestamp = Date(timeIntervalSince1970: TimeInterval(item.timestamp / 1000)) lazy var debugInfo: TimelineItemDebugInfo = { - let debugInfo = item.debugInfo() + let debugInfo = item.debugInfoProvider.get() return TimelineItemDebugInfo(model: debugInfo.model, originalJSON: debugInfo.originalJson, latestEditJSON: debugInfo.latestEditJson) }() - lazy var shieldState = item.getShield(strict: false) + lazy var shieldState = item.shieldsProvider.getShields(strict: false) - lazy var readReceipts = item.readReceipts() + lazy var readReceipts = item.readReceipts } struct TimelineItemDebugInfo: Identifiable, CustomStringConvertible { diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift index af817de161..837798cb49 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift @@ -22,6 +22,6 @@ struct PaginationIndicatorRoomTimelineItem: DecorationTimelineItemProtocol, Equa } init(position: Position) { - id = TimelineItemIdentifier(timelineID: position.id) + id = TimelineItemIdentifier(uniqueID: position.id) } } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/TimelineStartRoomTimelineItem.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/TimelineStartRoomTimelineItem.swift index 3aab2b6ba3..17435d9cce 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/TimelineStartRoomTimelineItem.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/TimelineStartRoomTimelineItem.swift @@ -8,6 +8,6 @@ import Foundation struct TimelineStartRoomTimelineItem: DecorationTimelineItemProtocol, Equatable { - let id = TimelineItemIdentifier(timelineID: UUID().uuidString) + let id = TimelineItemIdentifier(uniqueID: UUID().uuidString) let name: String? } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift index 018f47b837..75c1cfdd3f 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift @@ -27,7 +27,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { func buildTimelineItem(for eventItemProxy: EventTimelineItemProxy, isDM: Bool) -> RoomTimelineItemProtocol? { let isOutgoing = eventItemProxy.isOwn - switch eventItemProxy.content.kind() { + switch eventItemProxy.content { case .unableToDecrypt(let encryptedMessage): return buildEncryptedTimelineItem(eventItemProxy, encryptedMessage, isOutgoing) case .redactedMessage: @@ -43,8 +43,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { return buildUnsupportedTimelineItem(eventItemProxy, eventType, error, isOutgoing) case .failedToParseState(let eventType, _, let error): return buildUnsupportedTimelineItem(eventItemProxy, eventType, error, isOutgoing) - case .message: - return buildMessageTimelineItem(eventItemProxy, isOutgoing) + case .message(let messageContent): + return buildMessageTimelineItem(eventItemProxy, messageContent, isOutgoing) case .state(_, let content): if isDM, content == .roomCreate { return nil @@ -72,34 +72,29 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { } // MARK: - Message Events - - private func buildMessageTimelineItem(_ eventItemProxy: EventTimelineItemProxy, _ isOutgoing: Bool) -> RoomTimelineItemProtocol? { - guard let messageTimelineItem = eventItemProxy.content.asMessage() else { - fatalError("Invalid message timeline item: \(eventItemProxy)") - } - - let isThreaded = messageTimelineItem.isThreaded() - switch messageTimelineItem.msgtype() { - case .text(content: let content): - return buildTextTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded) - case .image(content: let content): - return buildImageTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded) - case .video(let content): - return buildVideoTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded) - case .file(let content): - return buildFileTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded) - case .notice(content: let content): - return buildNoticeTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded) - case .emote(content: let content): - return buildEmoteTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded) - case .audio(let content): - if content.voice != nil { - return buildVoiceTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded) + + private func buildMessageTimelineItem(_ eventItemProxy: EventTimelineItemProxy, _ messageContent: MessageContent, _ isOutgoing: Bool) -> RoomTimelineItemProtocol? { + switch messageContent.msgType { + case .text(content: let textMessageContent): + return buildTextTimelineItem(for: eventItemProxy, messageContent, textMessageContent, isOutgoing) + case .image(content: let imageMessageContent): + return buildImageTimelineItem(for: eventItemProxy, messageContent, imageMessageContent, isOutgoing) + case .video(let videoMessageContent): + return buildVideoTimelineItem(for: eventItemProxy, messageContent, videoMessageContent, isOutgoing) + case .file(let fileMessageContent): + return buildFileTimelineItem(for: eventItemProxy, messageContent, fileMessageContent, isOutgoing) + case .notice(content: let noticeMessageContent): + return buildNoticeTimelineItem(for: eventItemProxy, messageContent, noticeMessageContent, isOutgoing) + case .emote(content: let emoteMessageContent): + return buildEmoteTimelineItem(for: eventItemProxy, messageContent, emoteMessageContent, isOutgoing) + case .audio(let audioMessageContent): + if audioMessageContent.voice != nil { + return buildVoiceTimelineItem(for: eventItemProxy, messageContent, audioMessageContent, isOutgoing) } else { - return buildAudioTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded) + return buildAudioTimelineItem(for: eventItemProxy, messageContent, audioMessageContent, isOutgoing) } - case .location(let content): - return buildLocationTimelineItem(for: eventItemProxy, messageTimelineItem, content, isOutgoing, isThreaded) + case .location(let locationMessageContent): + return buildLocationTimelineItem(for: eventItemProxy, messageContent, locationMessageContent, isOutgoing) case .other: return nil } @@ -196,20 +191,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { } private func buildTextTimelineItem(for eventItemProxy: EventTimelineItemProxy, - _ messageTimelineItem: Message, - _ messageContent: TextMessageContent, - _ isOutgoing: Bool, - _ isThreaded: Bool) -> RoomTimelineItemProtocol { + _ messageContent: MessageContent, + _ textMessageContent: TextMessageContent, + _ isOutgoing: Bool) -> RoomTimelineItemProtocol { TextRoomTimelineItem(id: eventItemProxy.id, timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened), isOutgoing: isOutgoing, isEditable: eventItemProxy.isEditable, canBeRepliedTo: eventItemProxy.canBeRepliedTo, - isThreaded: isThreaded, + isThreaded: messageContent.threadRoot != nil, sender: eventItemProxy.sender, - content: buildTextTimelineItemContent(messageContent), - replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()), - properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(), + content: buildTextTimelineItemContent(textMessageContent), + replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo), + properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited, reactions: aggregateReactions(eventItemProxy.reactions), deliveryStatus: eventItemProxy.deliveryStatus, orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts), @@ -217,20 +211,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { } private func buildImageTimelineItem(for eventItemProxy: EventTimelineItemProxy, - _ messageTimelineItem: Message, - _ messageContent: ImageMessageContent, - _ isOutgoing: Bool, - _ isThreaded: Bool) -> RoomTimelineItemProtocol { + _ messageContent: MessageContent, + _ imageMessageContent: ImageMessageContent, + _ isOutgoing: Bool) -> RoomTimelineItemProtocol { ImageRoomTimelineItem(id: eventItemProxy.id, timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened), isOutgoing: isOutgoing, isEditable: eventItemProxy.isEditable, canBeRepliedTo: eventItemProxy.canBeRepliedTo, - isThreaded: isThreaded, + isThreaded: messageContent.threadRoot != nil, sender: eventItemProxy.sender, - content: buildImageTimelineItemContent(messageContent), - replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()), - properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(), + content: buildImageTimelineItemContent(imageMessageContent), + replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo), + properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited, reactions: aggregateReactions(eventItemProxy.reactions), deliveryStatus: eventItemProxy.deliveryStatus, orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts), @@ -238,20 +231,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { } private func buildVideoTimelineItem(for eventItemProxy: EventTimelineItemProxy, - _ messageTimelineItem: Message, - _ messageContent: VideoMessageContent, - _ isOutgoing: Bool, - _ isThreaded: Bool) -> RoomTimelineItemProtocol { + _ messageContent: MessageContent, + _ videoMessageContent: VideoMessageContent, + _ isOutgoing: Bool) -> RoomTimelineItemProtocol { VideoRoomTimelineItem(id: eventItemProxy.id, timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened), isOutgoing: isOutgoing, isEditable: eventItemProxy.isEditable, canBeRepliedTo: eventItemProxy.canBeRepliedTo, - isThreaded: isThreaded, + isThreaded: messageContent.threadRoot != nil, sender: eventItemProxy.sender, - content: buildVideoTimelineItemContent(messageContent), - replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()), - properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(), + content: buildVideoTimelineItemContent(videoMessageContent), + replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo), + properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited, reactions: aggregateReactions(eventItemProxy.reactions), deliveryStatus: eventItemProxy.deliveryStatus, orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts), @@ -259,20 +251,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { } private func buildAudioTimelineItem(for eventItemProxy: EventTimelineItemProxy, - _ messageTimelineItem: Message, - _ messageContent: AudioMessageContent, - _ isOutgoing: Bool, - _ isThreaded: Bool) -> RoomTimelineItemProtocol { + _ messageContent: MessageContent, + _ audioMessageContent: AudioMessageContent, + _ isOutgoing: Bool) -> RoomTimelineItemProtocol { AudioRoomTimelineItem(id: eventItemProxy.id, timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened), isOutgoing: isOutgoing, isEditable: eventItemProxy.isEditable, canBeRepliedTo: eventItemProxy.canBeRepliedTo, - isThreaded: isThreaded, + isThreaded: messageContent.threadRoot != nil, sender: eventItemProxy.sender, - content: buildAudioTimelineItemContent(messageContent), - replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()), - properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(), + content: buildAudioTimelineItemContent(audioMessageContent), + replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo), + properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited, reactions: aggregateReactions(eventItemProxy.reactions), deliveryStatus: eventItemProxy.deliveryStatus, orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts), @@ -280,20 +271,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { } private func buildVoiceTimelineItem(for eventItemProxy: EventTimelineItemProxy, - _ messageTimelineItem: Message, - _ messageContent: AudioMessageContent, - _ isOutgoing: Bool, - _ isThreaded: Bool) -> RoomTimelineItemProtocol { + _ messageContent: MessageContent, + _ audioMessageContent: AudioMessageContent, + _ isOutgoing: Bool) -> RoomTimelineItemProtocol { VoiceMessageRoomTimelineItem(id: eventItemProxy.id, timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened), isOutgoing: isOutgoing, isEditable: eventItemProxy.isEditable, canBeRepliedTo: eventItemProxy.canBeRepliedTo, - isThreaded: isThreaded, + isThreaded: messageContent.threadRoot != nil, sender: eventItemProxy.sender, - content: buildAudioTimelineItemContent(messageContent), - replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()), - properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(), + content: buildAudioTimelineItemContent(audioMessageContent), + replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo), + properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited, reactions: aggregateReactions(eventItemProxy.reactions), deliveryStatus: eventItemProxy.deliveryStatus, orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts), @@ -301,20 +291,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { } private func buildFileTimelineItem(for eventItemProxy: EventTimelineItemProxy, - _ messageTimelineItem: Message, - _ messageContent: FileMessageContent, - _ isOutgoing: Bool, - _ isThreaded: Bool) -> RoomTimelineItemProtocol { + _ messageContent: MessageContent, + _ fileMessageContent: FileMessageContent, + _ isOutgoing: Bool) -> RoomTimelineItemProtocol { FileRoomTimelineItem(id: eventItemProxy.id, timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened), isOutgoing: isOutgoing, isEditable: eventItemProxy.isEditable, canBeRepliedTo: eventItemProxy.canBeRepliedTo, - isThreaded: isThreaded, + isThreaded: messageContent.threadRoot != nil, sender: eventItemProxy.sender, - content: buildFileTimelineItemContent(messageContent), - replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()), - properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(), + content: buildFileTimelineItemContent(fileMessageContent), + replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo), + properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited, reactions: aggregateReactions(eventItemProxy.reactions), deliveryStatus: eventItemProxy.deliveryStatus, orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts), @@ -322,20 +311,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { } private func buildNoticeTimelineItem(for eventItemProxy: EventTimelineItemProxy, - _ messageTimelineItem: Message, - _ messageContent: NoticeMessageContent, - _ isOutgoing: Bool, - _ isThreaded: Bool) -> RoomTimelineItemProtocol { + _ messageContent: MessageContent, + _ noticeMessageContent: NoticeMessageContent, + _ isOutgoing: Bool) -> RoomTimelineItemProtocol { NoticeRoomTimelineItem(id: eventItemProxy.id, timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened), isOutgoing: isOutgoing, isEditable: eventItemProxy.isEditable, canBeRepliedTo: eventItemProxy.canBeRepliedTo, - isThreaded: isThreaded, + isThreaded: messageContent.threadRoot != nil, sender: eventItemProxy.sender, - content: buildNoticeTimelineItemContent(messageContent), - replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()), - properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(), + content: buildNoticeTimelineItemContent(noticeMessageContent), + replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo), + properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited, reactions: aggregateReactions(eventItemProxy.reactions), deliveryStatus: eventItemProxy.deliveryStatus, orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts), @@ -343,20 +331,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { } private func buildEmoteTimelineItem(for eventItemProxy: EventTimelineItemProxy, - _ messageTimelineItem: Message, - _ messageContent: EmoteMessageContent, - _ isOutgoing: Bool, - _ isThreaded: Bool) -> RoomTimelineItemProtocol { + _ messageContent: MessageContent, + _ emoteMessageContent: EmoteMessageContent, + _ isOutgoing: Bool) -> RoomTimelineItemProtocol { EmoteRoomTimelineItem(id: eventItemProxy.id, timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened), isOutgoing: isOutgoing, isEditable: eventItemProxy.isEditable, canBeRepliedTo: eventItemProxy.canBeRepliedTo, - isThreaded: isThreaded, + isThreaded: messageContent.threadRoot != nil, sender: eventItemProxy.sender, - content: buildEmoteTimelineItemContent(senderDisplayName: eventItemProxy.sender.displayName, senderID: eventItemProxy.sender.id, messageContent: messageContent), - replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()), - properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(), + content: buildEmoteTimelineItemContent(senderDisplayName: eventItemProxy.sender.displayName, senderID: eventItemProxy.sender.id, messageContent: emoteMessageContent), + replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo), + properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited, reactions: aggregateReactions(eventItemProxy.reactions), deliveryStatus: eventItemProxy.deliveryStatus, orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts), @@ -364,20 +351,19 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { } private func buildLocationTimelineItem(for eventItemProxy: EventTimelineItemProxy, - _ messageTimelineItem: Message, - _ messageContent: LocationContent, - _ isOutgoing: Bool, - _ isThreaded: Bool) -> RoomTimelineItemProtocol { + _ messageContent: MessageContent, + _ locationMessageContent: LocationContent, + _ isOutgoing: Bool) -> RoomTimelineItemProtocol { LocationRoomTimelineItem(id: eventItemProxy.id, timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened), isOutgoing: isOutgoing, isEditable: eventItemProxy.isEditable, canBeRepliedTo: eventItemProxy.canBeRepliedTo, - isThreaded: isThreaded, + isThreaded: messageContent.threadRoot != nil, sender: eventItemProxy.sender, - content: buildLocationTimelineItemContent(messageContent), - replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageTimelineItem.inReplyTo()), - properties: RoomTimelineItemProperties(isEdited: messageTimelineItem.isEdited(), + content: buildLocationTimelineItemContent(locationMessageContent), + replyDetails: buildReplyToDetailsFromDetailsIfAvailable(details: messageContent.inReplyTo), + properties: RoomTimelineItemProperties(isEdited: messageContent.isEdited, reactions: aggregateReactions(eventItemProxy.reactions), deliveryStatus: eventItemProxy.deliveryStatus, orderedReadReceipts: orderReadReceipts(eventItemProxy.readReceipts), @@ -652,12 +638,12 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { // MARK: - Reply details func buildReply(details: InReplyToDetails) -> TimelineItemReply { - let isThreaded = details.event.isThreaded - switch details.event { + let isThreaded = details.event().isThreaded + switch details.event() { case .unavailable: - return .init(details: .notLoaded(eventID: details.eventId), isThreaded: isThreaded) + return .init(details: .notLoaded(eventID: details.eventId()), isThreaded: isThreaded) case .pending: - return .init(details: .loading(eventID: details.eventId), isThreaded: isThreaded) + return .init(details: .loading(eventID: details.eventId()), isThreaded: isThreaded) case let .ready(timelineItem, senderID, senderProfile): let sender: TimelineItemSender switch senderProfile { @@ -675,9 +661,9 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { let replyContent: TimelineEventContent - switch timelineItem.kind() { - case .message: - return .init(details: timelineItemReplyDetails(sender: sender, eventID: details.eventId, messageType: timelineItem.asMessage()?.msgtype()), isThreaded: isThreaded) + switch timelineItem { + case .message(let messageContent): + return .init(details: timelineItemReplyDetails(sender: sender, eventID: details.eventId(), messageType: messageContent.msgType), isThreaded: isThreaded) case .poll(let question, _, _, _, _, _, _): replyContent = .poll(question: question) case .sticker(let body, _, _): @@ -688,9 +674,9 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { replyContent = .message(.text(.init(body: L10n.commonUnsupportedEvent))) } - return .init(details: .loaded(sender: sender, eventID: details.eventId, eventContent: replyContent), isThreaded: isThreaded) + return .init(details: .loaded(sender: sender, eventID: details.eventId(), eventContent: replyContent), isThreaded: isThreaded) case let .error(message): - return .init(details: .error(eventID: details.eventId, message: message), isThreaded: isThreaded) + return .init(details: .error(eventID: details.eventId(), message: message), isThreaded: isThreaded) } } @@ -751,7 +737,11 @@ private extension RepliedToEventDetails { var isThreaded: Bool { switch self { case .ready(let content, _, _): - return content.asMessage()?.isThreaded() ?? false + guard case let .message(content) = content else { + return false + } + + return content.threadRoot != nil default: return false } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemViewState.swift b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemViewState.swift index d3ae948287..5d5322a4d0 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemViewState.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemViewState.swift @@ -8,23 +8,14 @@ import Foundation final class RoomTimelineItemViewState: Identifiable, Equatable, ObservableObject { - static func == (lhs: RoomTimelineItemViewState, rhs: RoomTimelineItemViewState) -> Bool { - lhs.type == rhs.type && lhs.groupStyle == rhs.groupStyle - } - @Published var type: RoomTimelineItemType @Published var groupStyle: TimelineGroupStyle - /// Contains all the identification info of the item, `timelineID`, `eventID` and `transactionID` + /// Contains all the identification info of the item, `uniqueID`, `eventID` and `transactionID` var identifier: TimelineItemIdentifier { type.id } - /// The `timelineID` of the item, used for the timeline view level identification, do not use for any business logic use `identifier` instead - var id: String { - identifier.timelineID - } - init(type: RoomTimelineItemType, groupStyle: TimelineGroupStyle) { self.type = type self.groupStyle = groupStyle @@ -33,6 +24,19 @@ final class RoomTimelineItemViewState: Identifiable, Equatable, ObservableObject convenience init(item: RoomTimelineItemProtocol, groupStyle: TimelineGroupStyle) { self.init(type: .init(item: item), groupStyle: groupStyle) } + + // MARK: - Equatable + + static func == (lhs: RoomTimelineItemViewState, rhs: RoomTimelineItemViewState) -> Bool { + lhs.type == rhs.type && lhs.groupStyle == rhs.groupStyle + } + + // MARK: Identifiable + + /// The `timelineID` of the item, used for the timeline view level identification, do not use for any business logic use `identifier` instead + var id: String { + identifier.uniqueID + } } enum RoomTimelineItemType: Equatable { diff --git a/ElementX/Sources/Services/Timeline/TimelineProxy.swift b/ElementX/Sources/Services/Timeline/TimelineProxy.swift index a3bee43fdc..3d7c7ee806 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxy.swift @@ -72,7 +72,17 @@ final class TimelineProxy: TimelineProxyProtocol { } func messageEventContent(for timelineItemID: TimelineItemIdentifier) async -> RoomMessageEventContentWithoutRelation? { - await timelineProvider.itemProxies.firstEventTimelineItemUsingStableID(timelineItemID)?.content().asMessage()?.content() + guard let content = await timelineProvider.itemProxies.firstEventTimelineItemUsingStableID(timelineItemID)?.content, + case let .message(messageContent) = content else { + return nil + } + + do { + return try contentWithoutRelationFromMessage(message: messageContent) + } catch { + MXLog.error("Failed retrieving message event content for timelineItemID=\(timelineItemID)") + return nil + } } func paginateBackwards(requestSize: UInt16) async -> Result { @@ -156,15 +166,15 @@ final class TimelineProxy: TimelineProxyProtocol { func edit(_ timelineItem: EventTimelineItem, newContent: RoomMessageEventContentWithoutRelation) async -> Result { do { - guard try await timeline.edit(item: timelineItem, newContent: .roomMessage(content: newContent)) == true else { + guard try await timeline.edit(eventOrTransactionId: timelineItem.eventOrTransactionId, newContent: .roomMessage(content: newContent)) == true else { return .failure(.failedEditing) } - MXLog.info("Finished editing timeline item: \(timelineItem.eventId() ?? timelineItem.transactionId() ?? "unknown")") + MXLog.info("Finished editing timeline item: \(timelineItem.eventOrTransactionId)") return .success(()) } catch { - MXLog.error("Failed editing timeline item: \(timelineItem.eventId() ?? timelineItem.transactionId() ?? "unknown") with error: \(error)") + MXLog.error("Failed editing timeline item: \(timelineItem.eventOrTransactionId) with error: \(error)") return .failure(.sdkError(error)) } } @@ -172,18 +182,13 @@ final class TimelineProxy: TimelineProxyProtocol { func redact(_ timelineItemID: TimelineItemIdentifier, reason: String?) async -> Result { MXLog.info("Redacting timeline item: \(timelineItemID)") - guard let eventTimelineItem = await timelineProvider.itemProxies.firstEventTimelineItemUsingStableID(timelineItemID) else { + guard let eventOrTransactionID = await timelineProvider.itemProxies.firstEventTimelineItemUsingStableID(timelineItemID)?.eventOrTransactionId else { MXLog.error("Unknown timeline item: \(timelineItemID)") return .failure(.failedRedacting) } do { - let success = try await timeline.redactEvent(item: eventTimelineItem, reason: reason) - - guard success else { - MXLog.error("Failed redacting timeline item: \(timelineItemID)") - return .failure(.failedRedacting) - } + try await timeline.redactEvent(eventOrTransactionId: eventOrTransactionID, reason: reason) MXLog.info("Redacted timeline item: \(timelineItemID)") @@ -425,7 +430,7 @@ final class TimelineProxy: TimelineProxyProtocol { MXLog.info("Toggling reaction for event: \(itemID)") do { - try await timeline.toggleReaction(uniqueId: itemID.timelineID, key: reaction) + try await timeline.toggleReaction(uniqueId: itemID.uniqueID, key: reaction) MXLog.info("Finished toggling reaction for event: \(itemID)") return .success(()) } catch { @@ -460,7 +465,7 @@ final class TimelineProxy: TimelineProxyProtocol { do { let originalEvent = try await timeline.getEventTimelineItemByEventId(eventId: eventID) - guard try await timeline.edit(item: originalEvent, + guard try await timeline.edit(eventOrTransactionId: originalEvent.eventOrTransactionId, newContent: .pollStart(pollData: .init(question: question, answers: answers, maxSelections: 1, @@ -482,7 +487,7 @@ final class TimelineProxy: TimelineProxyProtocol { return await Task.dispatch(on: .global()) { do { - try self.timeline.endPoll(pollStartId: pollStartID, text: text) + try self.timeline.endPoll(pollStartEventId: pollStartID, text: text) MXLog.info("Finished ending poll with eventID: \(pollStartID)") @@ -498,7 +503,7 @@ final class TimelineProxy: TimelineProxyProtocol { MXLog.info("Sending response for poll with eventID: \(pollStartID)") do { - try await timeline.sendPollResponse(pollStartId: pollStartID, answers: answers) + try await timeline.sendPollResponse(pollStartEventId: pollStartID, answers: answers) MXLog.info("Finished sending response for poll with eventID: \(pollStartID)") @@ -618,7 +623,7 @@ extension Array where Element == TimelineItemProxy { func firstEventTimelineItemUsingStableID(_ id: TimelineItemIdentifier) -> EventTimelineItem? { for item in self { if case let .event(eventTimelineItem) = item { - if eventTimelineItem.id.timelineID == id.timelineID { + if eventTimelineItem.id.uniqueID == id.uniqueID { return eventTimelineItem.item } } diff --git a/UITests/Sources/PollFormScreenUITests.swift b/UITests/Sources/PollFormScreenUITests.swift index b2587b7483..fa02f72b9d 100644 --- a/UITests/Sources/PollFormScreenUITests.swift +++ b/UITests/Sources/PollFormScreenUITests.swift @@ -17,15 +17,15 @@ class PollFormScreenUITests: XCTestCase { func testFilledPoll() async throws { let app = Application.launch(.createPoll) let questionTextField = app.textViews[A11yIdentifiers.pollFormScreen.question] - questionTextField.forceTap() + questionTextField.tapCenter() questionTextField.typeText("Do you like polls?") let option1TextField = app.textViews[A11yIdentifiers.pollFormScreen.optionID(0)] - option1TextField.forceTap() + option1TextField.tapCenter() option1TextField.typeText("Yes") let option2TextField = app.textViews[A11yIdentifiers.pollFormScreen.optionID(1)] - option2TextField.forceTap() + option2TextField.tapCenter() option2TextField.typeText("No") // Dismiss the keyboard diff --git a/UITests/Sources/UserSessionScreenTests.swift b/UITests/Sources/UserSessionScreenTests.swift index bb8d05177b..942dae1fd8 100644 --- a/UITests/Sources/UserSessionScreenTests.swift +++ b/UITests/Sources/UserSessionScreenTests.swift @@ -20,7 +20,7 @@ class UserSessionScreenTests: XCTestCase { try await Task.sleep(for: .seconds(1)) try await app.assertScreenshot(.userSessionScreen, step: 2) - app.buttons[A11yIdentifiers.roomScreen.composerToolbar.openComposeOptions].forceTap() + app.buttons[A11yIdentifiers.roomScreen.composerToolbar.openComposeOptions].tapCenter() try await app.assertScreenshot(.userSessionScreen, step: 3) } diff --git a/UnitTests/Sources/ComposerToolbarViewModelTests.swift b/UnitTests/Sources/ComposerToolbarViewModelTests.swift index d788534884..d4bf5fae0a 100644 --- a/UnitTests/Sources/ComposerToolbarViewModelTests.swift +++ b/UnitTests/Sources/ComposerToolbarViewModelTests.swift @@ -41,14 +41,14 @@ class ComposerToolbarViewModelTests: XCTestCase { } func testComposerFocus() { - viewModel.process(timelineAction: .setMode(mode: .edit(originalItemId: TimelineItemIdentifier(timelineID: "mock")))) + viewModel.process(timelineAction: .setMode(mode: .edit(originalItemId: TimelineItemIdentifier(uniqueID: "mock")))) XCTAssertTrue(viewModel.state.bindings.composerFocused) viewModel.process(timelineAction: .removeFocus) XCTAssertFalse(viewModel.state.bindings.composerFocused) } func testComposerMode() { - let mode: ComposerMode = .edit(originalItemId: TimelineItemIdentifier(timelineID: "mock")) + let mode: ComposerMode = .edit(originalItemId: TimelineItemIdentifier(uniqueID: "mock")) viewModel.process(timelineAction: .setMode(mode: mode)) XCTAssertEqual(viewModel.state.composerMode, mode) viewModel.process(timelineAction: .clear) @@ -56,7 +56,7 @@ class ComposerToolbarViewModelTests: XCTestCase { } func testComposerModeIsPublished() { - let mode: ComposerMode = .edit(originalItemId: TimelineItemIdentifier(timelineID: "mock")) + let mode: ComposerMode = .edit(originalItemId: TimelineItemIdentifier(uniqueID: "mock")) let expectation = expectation(description: "Composer mode is published") let cancellable = viewModel .context @@ -236,7 +236,7 @@ class ComposerToolbarViewModelTests: XCTestCase { } viewModel.context.composerFormattingEnabled = false - viewModel.process(timelineAction: .setMode(mode: .edit(originalItemId: .init(timelineID: "", eventID: "testID")))) + viewModel.process(timelineAction: .setMode(mode: .edit(originalItemId: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "testID"))))) viewModel.context.plainComposerText = .init(string: "Hello world!") viewModel.saveDraft() @@ -257,12 +257,11 @@ class ComposerToolbarViewModelTests: XCTestCase { } viewModel.context.composerFormattingEnabled = false - viewModel.process(timelineAction: .setMode(mode: .reply(itemID: .init(timelineID: "", - eventID: "testID"), - replyDetails: .loaded(sender: .init(id: ""), - eventID: "testID", - eventContent: .message(.text(.init(body: "reply text")))), - isThread: false))) + viewModel.process(timelineAction: .setMode(mode: .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "testID")), + replyDetails: .loaded(sender: .init(id: ""), + eventID: "testID", + eventContent: .message(.text(.init(body: "reply text")))), + isThread: false))) viewModel.context.plainComposerText = .init(string: "Hello world!") viewModel.saveDraft() @@ -283,12 +282,11 @@ class ComposerToolbarViewModelTests: XCTestCase { } viewModel.context.composerFormattingEnabled = false - viewModel.process(timelineAction: .setMode(mode: .reply(itemID: .init(timelineID: "", - eventID: "testID"), - replyDetails: .loaded(sender: .init(id: ""), - eventID: "testID", - eventContent: .message(.text(.init(body: "reply text")))), - isThread: false))) + viewModel.process(timelineAction: .setMode(mode: .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "testID")), + replyDetails: .loaded(sender: .init(id: ""), + eventID: "testID", + eventContent: .message(.text(.init(body: "reply text")))), + isThread: false))) viewModel.saveDraft() await fulfillment(of: [expectation], timeout: 10) @@ -397,7 +395,7 @@ class ComposerToolbarViewModelTests: XCTestCase { await fulfillment(of: [expectation], timeout: 10) XCTAssertFalse(viewModel.context.composerFormattingEnabled) - XCTAssertEqual(viewModel.state.composerMode, .edit(originalItemId: .init(timelineID: "", eventID: "testID"))) + XCTAssertEqual(viewModel.state.composerMode, .edit(originalItemId: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "testID")))) XCTAssertEqual(viewModel.context.plainComposerText, NSAttributedString(string: "Hello world!")) } @@ -431,13 +429,13 @@ class ComposerToolbarViewModelTests: XCTestCase { await fulfillment(of: [draftExpectation], timeout: 10) XCTAssertFalse(viewModel.context.composerFormattingEnabled) // Testing the loading state first - XCTAssertEqual(viewModel.state.composerMode, .reply(itemID: .init(timelineID: "", eventID: testEventID), + XCTAssertEqual(viewModel.state.composerMode, .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: testEventID)), replyDetails: .loading(eventID: testEventID), isThread: false)) XCTAssertEqual(viewModel.context.plainComposerText, NSAttributedString(string: text)) await fulfillment(of: [loadReplyExpectation], timeout: 10) - XCTAssertEqual(viewModel.state.composerMode, .reply(itemID: .init(timelineID: "", eventID: testEventID), + XCTAssertEqual(viewModel.state.composerMode, .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: testEventID)), replyDetails: loadedReply, isThread: true)) } @@ -445,8 +443,7 @@ class ComposerToolbarViewModelTests: XCTestCase { func testRestoreReplyAndCancelReplyMode() async { let testEventID = "testID" let text = "Hello world!" - let loadedReply = TimelineItemReplyDetails.loaded(sender: .init(id: "userID", - displayName: "Username"), + let loadedReply = TimelineItemReplyDetails.loaded(sender: .init(id: "userID", displayName: "Username"), eventID: testEventID, eventContent: .message(.text(.init(body: "Reply text")))) @@ -472,7 +469,7 @@ class ComposerToolbarViewModelTests: XCTestCase { await fulfillment(of: [draftExpectation], timeout: 10) XCTAssertFalse(viewModel.context.composerFormattingEnabled) // Testing the loading state first - XCTAssertEqual(viewModel.state.composerMode, .reply(itemID: .init(timelineID: "", eventID: testEventID), + XCTAssertEqual(viewModel.state.composerMode, .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: testEventID)), replyDetails: .loading(eventID: testEventID), isThread: false)) XCTAssertEqual(viewModel.context.plainComposerText, NSAttributedString(string: text)) diff --git a/UnitTests/Sources/LoggingTests.swift b/UnitTests/Sources/LoggingTests.swift index df701300a7..99019ac587 100644 --- a/UnitTests/Sources/LoggingTests.swift +++ b/UnitTests/Sources/LoggingTests.swift @@ -142,7 +142,7 @@ class LoggingTests: XCTestCase { isThreaded: false, sender: .init(id: "sender"), content: .init(body: "EmoteString", formattedBody: AttributedString(emoteAttributedString))) - let imageMessage = ImageRoomTimelineItem(id: .init(timelineID: "myimagemessage"), + let imageMessage = ImageRoomTimelineItem(id: .init(uniqueID: "myimagemessage"), timestamp: "", isOutgoing: false, isEditable: false, @@ -184,25 +184,25 @@ class LoggingTests: XCTestCase { } let content = try String(contentsOf: logFile) - XCTAssertTrue(content.contains(textMessage.id.timelineID)) + XCTAssertTrue(content.contains(textMessage.id.uniqueID)) XCTAssertFalse(content.contains(textMessage.body)) XCTAssertFalse(content.contains(textAttributedString)) - XCTAssertTrue(content.contains(noticeMessage.id.timelineID)) + XCTAssertTrue(content.contains(noticeMessage.id.uniqueID)) XCTAssertFalse(content.contains(noticeMessage.body)) XCTAssertFalse(content.contains(noticeAttributedString)) - XCTAssertTrue(content.contains(emoteMessage.id.timelineID)) + XCTAssertTrue(content.contains(emoteMessage.id.uniqueID)) XCTAssertFalse(content.contains(emoteMessage.body)) XCTAssertFalse(content.contains(emoteAttributedString)) - XCTAssertTrue(content.contains(imageMessage.id.timelineID)) + XCTAssertTrue(content.contains(imageMessage.id.uniqueID)) XCTAssertFalse(content.contains(imageMessage.body)) - XCTAssertTrue(content.contains(videoMessage.id.timelineID)) + XCTAssertTrue(content.contains(videoMessage.id.uniqueID)) XCTAssertFalse(content.contains(videoMessage.body)) - XCTAssertTrue(content.contains(fileMessage.id.timelineID)) + XCTAssertTrue(content.contains(fileMessage.id.uniqueID)) XCTAssertFalse(content.contains(fileMessage.body)) } diff --git a/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift b/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift index 292d85d1f1..e1705b3882 100644 --- a/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift +++ b/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift @@ -12,7 +12,7 @@ import XCTest @MainActor class MessageForwardingScreenViewModelTests: XCTestCase { - let forwardingItem = MessageForwardingItem(id: .init(timelineID: "t1", eventID: "t1"), + let forwardingItem = MessageForwardingItem(id: .init(uniqueID: "t1", eventOrTransactionID: .eventId(eventId: "t1")), roomID: "1", content: .init(noPointer: .init())) var viewModel: MessageForwardingScreenViewModelProtocol! diff --git a/UnitTests/Sources/RoomScreenViewModelTests.swift b/UnitTests/Sources/RoomScreenViewModelTests.swift index ab52a3cd1c..20207bf013 100644 --- a/UnitTests/Sources/RoomScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomScreenViewModelTests.swift @@ -6,6 +6,7 @@ // @testable import ElementX +import MatrixRustSDK import Combine import XCTest @@ -66,8 +67,8 @@ class RoomScreenViewModelTests: XCTestCase { let providerUpdateSubject = PassthroughSubject<([TimelineItemProxy], PaginationState), Never>() pinnedTimelineProviderMock.underlyingUpdatePublisher = providerUpdateSubject.eraseToAnyPublisher() pinnedTimelineMock.timelineProvider = pinnedTimelineProviderMock - pinnedTimelineProviderMock.itemProxies = [.event(.init(item: EventTimelineItemSDKMock(configuration: .init(eventID: "test1")), id: "1")), - .event(.init(item: EventTimelineItemSDKMock(configuration: .init(eventID: "test2")), id: "2"))] + pinnedTimelineProviderMock.itemProxies = [.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test1")), uniqueID: "1")), + .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test2")), uniqueID: "2"))] // check if the banner is now in a loaded state and is showing the counter deferred = deferFulfillment(viewModel.context.$viewState) { viewState in @@ -83,9 +84,9 @@ class RoomScreenViewModelTests: XCTestCase { deferred = deferFulfillment(viewModel.context.$viewState) { viewState in viewState.pinnedEventsBannerState.count == 3 } - providerUpdateSubject.send(([.event(.init(item: EventTimelineItemSDKMock(configuration: .init(eventID: "test1")), id: "1")), - .event(.init(item: EventTimelineItemSDKMock(configuration: .init(eventID: "test2")), id: "2")), - .event(.init(item: EventTimelineItemSDKMock(configuration: .init(eventID: "test3")), id: "3"))], .initial)) + providerUpdateSubject.send(([.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test1")), uniqueID: "1")), + .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test2")), uniqueID: "2")), + .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test3")), uniqueID: "3"))], .initial)) try await deferred.fulfill() XCTAssertFalse(viewModel.context.viewState.pinnedEventsBannerState.isLoading) XCTAssertTrue(viewModel.context.viewState.shouldShowPinnedEventsBanner) @@ -106,9 +107,9 @@ class RoomScreenViewModelTests: XCTestCase { let pinnedTimelineProviderMock = RoomTimelineProviderMock() pinnedTimelineMock.timelineProvider = pinnedTimelineProviderMock pinnedTimelineProviderMock.underlyingUpdatePublisher = Empty<([TimelineItemProxy], PaginationState), Never>().eraseToAnyPublisher() - pinnedTimelineProviderMock.itemProxies = [.event(.init(item: EventTimelineItemSDKMock(configuration: .init(eventID: "test1")), id: "1")), - .event(.init(item: EventTimelineItemSDKMock(configuration: .init(eventID: "test2")), id: "2")), - .event(.init(item: EventTimelineItemSDKMock(configuration: .init(eventID: "test3")), id: "3"))] + pinnedTimelineProviderMock.itemProxies = [.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test1")), uniqueID: "1")), + .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test2")), uniqueID: "2")), + .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test3")), uniqueID: "3"))] roomProxyMock.underlyingPinnedEventsTimeline = pinnedTimelineMock let viewModel = RoomScreenViewModel(roomProxy: roomProxyMock, initialSelectedPinnedEventID: "test1", diff --git a/UnitTests/Sources/TimelineItemFactoryTests.swift b/UnitTests/Sources/TimelineItemFactoryTests.swift index ec352957e1..0ca14f8fac 100644 --- a/UnitTests/Sources/TimelineItemFactoryTests.swift +++ b/UnitTests/Sources/TimelineItemFactoryTests.swift @@ -6,6 +6,8 @@ // @testable import ElementX +import MatrixRustSDK + import XCTest @MainActor @@ -18,19 +20,9 @@ class TimelineItemFactoryTests: XCTestCase { attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), stateEventStringBuilder: RoomStateEventStringBuilder(userID: ownUserID)) - let eventTimelineItem = EventTimelineItemSDKMock() - eventTimelineItem.isOwnReturnValue = true - eventTimelineItem.timestampReturnValue = 0 - eventTimelineItem.isEditableReturnValue = false - eventTimelineItem.canBeRepliedToReturnValue = false - eventTimelineItem.senderReturnValue = senderUserID - eventTimelineItem.senderProfileReturnValue = .pending - - let timelineItemContent = TimelineItemContentSDKMock() - timelineItemContent.kindReturnValue = .callInvite - eventTimelineItem.contentReturnValue = timelineItemContent + let eventTimelineItem = EventTimelineItem.mockCallInvite(sender: senderUserID) - let eventTimelineItemProxy = EventTimelineItemProxy(item: eventTimelineItem, id: "0") + let eventTimelineItemProxy = EventTimelineItemProxy(item: eventTimelineItem, uniqueID: "0") let item = factory.buildTimelineItem(for: eventTimelineItemProxy, isDM: false) diff --git a/UnitTests/Sources/TimelineViewModelTests.swift b/UnitTests/Sources/TimelineViewModelTests.swift index 1243d52918..062aff86c7 100644 --- a/UnitTests/Sources/TimelineViewModelTests.swift +++ b/UnitTests/Sources/TimelineViewModelTests.swift @@ -258,9 +258,9 @@ class TimelineViewModelTests: XCTestCase { func testSendReadReceiptWithoutEvents() async throws { // Given a room with only virtual items. - let items = [SeparatorRoomTimelineItem(timelineID: "v1"), - SeparatorRoomTimelineItem(timelineID: "v2"), - SeparatorRoomTimelineItem(timelineID: "v3")] + let items = [SeparatorRoomTimelineItem(uniqueID: "v1"), + SeparatorRoomTimelineItem(uniqueID: "v2"), + SeparatorRoomTimelineItem(uniqueID: "v3")] let (viewModel, _, timelineProxy, _) = readReceiptsConfiguration(with: items) // When sending a read receipt for the last item. @@ -275,7 +275,7 @@ class TimelineViewModelTests: XCTestCase { // Given a room where the last event is a virtual item. let items: [RoomTimelineItemProtocol] = [TextRoomTimelineItem(eventID: "t1"), TextRoomTimelineItem(eventID: "t2"), - SeparatorRoomTimelineItem(timelineID: "v3")] + SeparatorRoomTimelineItem(uniqueID: "v3")] let (viewModel, _, _, _) = readReceiptsConfiguration(with: items) // When sending a read receipt for the last item. @@ -436,14 +436,14 @@ private extension TextRoomTimelineItem { } private extension SeparatorRoomTimelineItem { - init(timelineID: String) { - self.init(id: .init(timelineID: timelineID), text: "") + init(uniqueID: String) { + self.init(id: .init(uniqueID: uniqueID), text: "") } } private extension TextRoomTimelineItem { init(eventID: String) { - self.init(id: .init(timelineID: UUID().uuidString, eventID: eventID), + self.init(id: .init(uniqueID: UUID().uuidString, eventOrTransactionID: .eventId(eventId: eventID)), timestamp: "", isOutgoing: false, isEditable: false, diff --git a/project.yml b/project.yml index 281902718b..cc0087e341 100644 --- a/project.yml +++ b/project.yml @@ -60,7 +60,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/element-hq/matrix-rust-components-swift - exactVersion: 1.0.52 + exactVersion: 1.0.53 # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios From e0ee4be834514cfd935eecb195ee4bac037fcbfc Mon Sep 17 00:00:00 2001 From: Mauro <34335419+Velin92@users.noreply.github.com> Date: Wed, 2 Oct 2024 16:30:07 +0200 Subject: [PATCH 025/114] use element-hq RTE version (#3360) --- ElementX.xcodeproj/project.pbxproj | 20 +- .../xcshareddata/swiftpm/Package.resolved | 8 +- .../matrix-rich-text-editor-swift.plist | 862 ++++++++++++++---- project.yml | 4 +- 4 files changed, 677 insertions(+), 217 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 29406d27a5..221259ea60 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -5795,7 +5795,7 @@ E2F3DA35D462724CCC61DE2C /* XCRemoteSwiftPackageReference "swift-ogg" */, 6582B5AF3F104B0F7E031E7D /* XCRemoteSwiftPackageReference "SwiftState" */, EC6D0C817B1C21D9D096505A /* XCRemoteSwiftPackageReference "Version" */, - 44FA555384AD79668D886043 /* XCRemoteSwiftPackageReference "matrix-rich-text-editor-swift" */, + EE40B0E16A55BD23ECBFFD22 /* XCRemoteSwiftPackageReference "matrix-rich-text-editor-swift" */, ); projectDirPath = ""; projectRoot = ""; @@ -7750,14 +7750,6 @@ minimumVersion = 1.2.0; }; }; - 44FA555384AD79668D886043 /* XCRemoteSwiftPackageReference "matrix-rich-text-editor-swift" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/matrix-org/matrix-rich-text-editor-swift"; - requirement = { - kind = exactVersion; - version = 2.37.7; - }; - }; 4BDA7F6042968E8422470F3F /* XCRemoteSwiftPackageReference "LoremSwiftum" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/lukaskubanek/LoremSwiftum"; @@ -7902,6 +7894,14 @@ minimumVersion = 2.1.0; }; }; + EE40B0E16A55BD23ECBFFD22 /* XCRemoteSwiftPackageReference "matrix-rich-text-editor-swift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/element-hq/matrix-rich-text-editor-swift"; + requirement = { + kind = exactVersion; + version = 2.37.12; + }; + }; F71C70A4404CC6D9C4AF35F2 /* XCRemoteSwiftPackageReference "compound-ios" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/element-hq/compound-ios"; @@ -8178,7 +8178,7 @@ }; CA07D57389DACE18AEB6A5E2 /* WysiwygComposer */ = { isa = XCSwiftPackageProductDependency; - package = 44FA555384AD79668D886043 /* XCRemoteSwiftPackageReference "matrix-rich-text-editor-swift" */; + package = EE40B0E16A55BD23ECBFFD22 /* XCRemoteSwiftPackageReference "matrix-rich-text-editor-swift" */; productName = WysiwygComposer; }; CCE5BF78B125320CBF3BB834 /* PostHog */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index eba7ed56ab..77d6b4c460 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "52385e2a478cc9455693d0b93dc33b988ac1d4742acceeaee288944dff8b78e0", + "originHash" : "f9011692b20e61e6f0df94b6e0946a4c8f4d58429a88998f249712bb1fee47f1", "pins" : [ { "identity" : "compound-design-tokens", @@ -138,10 +138,10 @@ { "identity" : "matrix-rich-text-editor-swift", "kind" : "remoteSourceControl", - "location" : "https://github.com/matrix-org/matrix-rich-text-editor-swift", + "location" : "https://github.com/element-hq/matrix-rich-text-editor-swift", "state" : { - "revision" : "30909decc48be8e06c93eacffa4ec71b539888a9", - "version" : "2.37.7" + "revision" : "b6583a47b5d14d2dc8405a0303ebd4041b877707", + "version" : "2.37.12" } }, { diff --git a/ElementX/SupportingFiles/Settings.bundle/Packages/matrix-rich-text-editor-swift.plist b/ElementX/SupportingFiles/Settings.bundle/Packages/matrix-rich-text-editor-swift.plist index 9495e4d817..696374fabc 100644 --- a/ElementX/SupportingFiles/Settings.bundle/Packages/matrix-rich-text-editor-swift.plist +++ b/ElementX/SupportingFiles/Settings.bundle/Packages/matrix-rich-text-editor-swift.plist @@ -6,207 +6,667 @@ FooterText - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +<https://www.gnu.org/licenses/>. Type PSGroupSpecifier diff --git a/project.yml b/project.yml index cc0087e341..48265b0c99 100644 --- a/project.yml +++ b/project.yml @@ -78,8 +78,8 @@ packages: minorVersion: 0.0.3 # path: ../swift-ogg WysiwygComposer: - url: https://github.com/matrix-org/matrix-rich-text-editor-swift - exactVersion: 2.37.7 + url: https://github.com/element-hq/matrix-rich-text-editor-swift + exactVersion: 2.37.12 # path: ../matrix-rich-text-editor/platforms/ios/lib/WysiwygComposer # External dependencies From 77e4fae2fb879d8daec1aaa318ff22e5bc4b3fba Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 1 Oct 2024 13:34:16 +0100 Subject: [PATCH 026/114] Use the new @Entry macro everywhere. Fix analytics service optionality. --- .../RoomScreen/RoomScreenViewModel.swift | 12 ------------ .../Screens/Timeline/TimelineViewModel.swift | 19 ++----------------- .../Timeline/View/Style/TimelineStyle.swift | 9 +-------- .../Analytics/Helpers/Analytics+SwiftUI.swift | 9 +-------- 4 files changed, 4 insertions(+), 45 deletions(-) diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index 990d304081..b58e04c1a7 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -203,15 +203,3 @@ extension RoomScreenViewModel { analyticsService: ServiceLocator.shared.analytics) } } - -private struct RoomContextKey: EnvironmentKey { - @MainActor static let defaultValue: RoomScreenViewModel.Context? = nil -} - -extension EnvironmentValues { - /// Used to access and inject the room context without observing it - var roomContext: RoomScreenViewModel.Context? { - get { self[RoomContextKey.self] } - set { self[RoomContextKey.self] = newValue } - } -} diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index e6d37a7dfa..517cc7d568 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -865,26 +865,11 @@ extension TimelineViewModel { analyticsService: ServiceLocator.shared.analytics) } -private struct TimelineContextKey: EnvironmentKey { - @MainActor static let defaultValue: TimelineViewModel.Context? = nil -} - -private struct FocussedEventID: EnvironmentKey { - static let defaultValue: String? = nil -} - extension EnvironmentValues { /// Used to access and inject the room context without observing it - var timelineContext: TimelineViewModel.Context? { - get { self[TimelineContextKey.self] } - set { self[TimelineContextKey.self] = newValue } - } - + @Entry var timelineContext: TimelineViewModel.Context? /// An event ID which will be non-nil when a timeline item should show as focussed. - var focussedEventID: String? { - get { self[FocussedEventID.self] } - set { self[FocussedEventID.self] = newValue } - } + @Entry var focussedEventID: String? } private enum SlashCommand: String, CaseIterable { diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyle.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyle.swift index b8294a041c..7726cee0f3 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyle.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyle.swift @@ -26,13 +26,6 @@ enum TimelineGroupStyle: Hashable { // MARK: - Environment -private struct TimelineGroupStyleKey: EnvironmentKey { - static let defaultValue = TimelineGroupStyle.single -} - extension EnvironmentValues { - var timelineGroupStyle: TimelineGroupStyle { - get { self[TimelineGroupStyleKey.self] } - set { self[TimelineGroupStyleKey.self] = newValue } - } + @Entry var timelineGroupStyle: TimelineGroupStyle = .single } diff --git a/ElementX/Sources/Services/Analytics/Helpers/Analytics+SwiftUI.swift b/ElementX/Sources/Services/Analytics/Helpers/Analytics+SwiftUI.swift index cdc573bc69..2c5247b868 100644 --- a/ElementX/Sources/Services/Analytics/Helpers/Analytics+SwiftUI.swift +++ b/ElementX/Sources/Services/Analytics/Helpers/Analytics+SwiftUI.swift @@ -7,13 +7,6 @@ import SwiftUI -private struct AnalyticsServiceKey: EnvironmentKey { - static let defaultValue: AnalyticsService = ServiceLocator.shared.analytics -} - extension EnvironmentValues { - var analyticsService: AnalyticsService { - get { self[AnalyticsServiceKey.self] } - set { self[AnalyticsServiceKey.self] = newValue } - } + @Entry var analyticsService: AnalyticsService = ServiceLocator.shared.analytics } From 5522871dd98ffb1a3e9783c69badbc783af4a8a7 Mon Sep 17 00:00:00 2001 From: Doug Date: Wed, 2 Oct 2024 17:41:08 +0100 Subject: [PATCH 027/114] =?UTF-8?q?MockMediaProvider=20=E2=86=92=20MediaPr?= =?UTF-8?q?oviderMock.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ElementX.xcodeproj/project.pbxproj | 10 +- .../Mocks/Generated/GeneratedMocks.swift | 423 ++++++++++++++++++ .../Sources/Mocks/MediaProviderMock.swift | 57 +++ ElementX/Sources/Mocks/UserSessionMock.swift | 2 +- .../Pills/PillAttachmentViewProvider.swift | 2 +- ElementX/Sources/Other/Pills/PillView.swift | 2 +- .../SwiftUI/Views/AvatarHeaderView.swift | 8 +- .../Other/SwiftUI/Views/RoomAvatarImage.swift | 10 +- .../SwiftUI/Views/RoomInviterLabel.swift | 8 +- .../SwiftUI/Views/UserProfileListRow.swift | 10 +- .../View/BlockedUsersScreen.swift | 2 +- .../View/GlobalSearchScreen.swift | 2 +- .../View/GlobalSearchScreenCell.swift | 2 +- .../View/InviteUsersScreen.swift | 2 +- .../View/InviteUsersScreenSelectedItem.swift | 2 +- .../JoinRoomScreen/View/JoinRoomScreen.swift | 2 +- .../View/MessageForwardingScreen.swift | 2 +- .../View/PinnedEventsTimelineScreen.swift | 2 +- .../View/RoomChangeRolesScreen.swift | 2 +- .../View/RoomChangeRolesScreenRow.swift | 12 +- .../RoomChangeRolesScreenSelectedItem.swift | 2 +- .../View/RoomDetailsEditScreen.swift | 4 +- .../View/RoomDetailsScreen.swift | 6 +- .../View/RoomDirectoryCell.swift | 16 +- .../View/RoomDirectorySearchScreen.swift | 2 +- .../View/RoomMemberDetailsScreen.swift | 2 +- .../RoomMembersListManageMemberSheet.swift | 2 +- .../View/RoomMembersListScreen.swift | 2 +- .../RoomMembersListScreenMemberCell.swift | 2 +- .../View/CompletionSuggestionView.swift | 4 +- .../View/ComposerToolbar.swift | 12 +- .../View/MentionSuggestionItemView.swift | 2 +- .../View/RoomAttachmentPicker.swift | 2 +- .../RoomScreen/RoomScreenViewModel.swift | 2 +- .../RoomScreen/View/RoomHeaderView.swift | 4 +- .../Screens/RoomScreen/View/RoomScreen.swift | 2 +- .../View/UserDetailsEditScreen.swift | 2 +- .../Screens/Timeline/TimelineViewModel.swift | 4 +- .../View/ReadReceipts/ReadReceiptCell.swift | 6 +- .../ReadReceiptsSummaryView.swift | 2 +- .../Style/TimelineItemBubbledStylerView.swift | 2 +- .../Supplementary/ReactionsSummaryView.swift | 2 +- .../TimelineReadReceiptsView.swift | 2 +- .../HighlightedTimelineItemModifier.swift | 2 +- .../Screens/Timeline/View/TimelineView.swift | 2 +- .../View/UserProfileScreen.swift | 2 +- .../Provider/MediaProviderProtocol.swift | 1 + .../Media/Provider/MockMediaProvider.swift | 62 --- .../UITests/UITestsAppCoordinator.swift | 30 +- .../BlockedUsersScreenViewModelTests.swift | 4 +- .../ComposerToolbarViewModelTests.swift | 4 +- .../GlobalSearchScreenViewModelTests.swift | 2 +- .../Sources/InviteUsersViewModelTests.swift | 2 +- .../JoinRoomScreenViewModelTests.swift | 2 +- ...essageForwardingScreenViewModelTests.swift | 2 +- UnitTests/Sources/PillContextTests.swift | 6 +- .../RoomChangeRolesScreenViewModelTests.swift | 2 +- .../RoomDetailsEditScreenViewModelTests.swift | 2 +- .../Sources/RoomDetailsViewModelTests.swift | 34 +- .../RoomMemberDetailsViewModelTests.swift | 14 +- .../RoomMembersListScreenViewModelTests.swift | 2 +- .../Sources/RoomScreenViewModelTests.swift | 8 +- .../Sources/TimelineViewModelTests.swift | 10 +- .../UserProfileScreenViewModelTests.swift | 4 +- .../VoiceMessageMediaManagerTests.swift | 10 +- 65 files changed, 636 insertions(+), 219 deletions(-) create mode 100644 ElementX/Sources/Mocks/MediaProviderMock.swift delete mode 100644 ElementX/Sources/Services/Media/Provider/MockMediaProvider.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 221259ea60..d0a5dfe789 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -322,6 +322,7 @@ 4715FE33667C5899E64DD0E6 /* ResolveVerifiedUserSendFailureScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97287090CA64DAA95386ECED /* ResolveVerifiedUserSendFailureScreen.swift */; }; 4716587A9BA69ED8FD1B986B /* PollOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6B19D10B102956066AF117B /* PollOptionView.swift */; }; 47305C0911C9E1AA774A4000 /* TemplateScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA90BD288E5AE6BC643AFDDF /* TemplateScreenCoordinator.swift */; }; + 478C363F63A5DFC3C83E334C /* MediaProviderMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F65E4BB9E82EB8373207CF8 /* MediaProviderMock.swift */; }; 4799A852132F1744E2825994 /* CreateRoomViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 340179A0FC1AD4AEDA7FC134 /* CreateRoomViewModelProtocol.swift */; }; 4807E8F51DB54F56B25E1C7E /* AppLockSetupSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D8C38663020DF2EB2D13F5E /* AppLockSetupSettingsScreenViewModel.swift */; }; 48416BBEB8DDF3E4DED0EDB6 /* ElementCallServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FC8B21E86B137BE4E91F82A /* ElementCallServiceProtocol.swift */; }; @@ -360,7 +361,6 @@ 4EA1CE0E88EA68E862FF0EA2 /* NotificationSettingsEditScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B564D748B67A156F413CD97 /* NotificationSettingsEditScreenModels.swift */; }; 4EAC427267424192964B16B3 /* AppSettingsHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13BE9781699FB510E9263192 /* AppSettingsHook.swift */; }; 4F2DF6138E87A4B8C2488CA3 /* VoiceMessageCacheProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43A84EE187D0C772E18A4E39 /* VoiceMessageCacheProtocol.swift */; }; - 4FC085B1E5D1EB804495E2F4 /* MockMediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FD6E621CC5E6D4830D96D2D /* MockMediaProvider.swift */; }; 4FDC8A9764CFDA90CE035725 /* Duration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FB2253D36E81E045E1CB432 /* Duration.swift */; }; 4FE688FE9375B2FBF424146A /* TextBasedRoomTimelineViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6EA0D8B0BBD8805F7D5A133 /* TextBasedRoomTimelineViewProtocol.swift */; }; 4FF90E2242DBD596E1ED2E27 /* AppCoordinatorStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077D7C3BE199B6E5DDEC07EC /* AppCoordinatorStateMachine.swift */; }; @@ -824,7 +824,6 @@ B5E455C9689EA600EDB3E9E0 /* NavigationRootCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA28F29C9F93E93CC3C2C715 /* NavigationRootCoordinator.swift */; }; B6048166B4AA4CEFEA9B77A6 /* InfoPlistReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A580295A56B55A856CC4084 /* InfoPlistReader.swift */; }; B6064D82FCDCB829601C1F59 /* SecureBackupLogoutConfirmationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FEE10AB666891E6A675E5E /* SecureBackupLogoutConfirmationScreen.swift */; }; - B659E3A49889E749E3239EA7 /* MockMediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FD6E621CC5E6D4830D96D2D /* MockMediaProvider.swift */; }; B6DA66EFC13A90846B625836 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 91DE43B8815918E590912DDA /* InfoPlist.strings */; }; B6DF6B6FA8734B70F9BF261E /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5272BC4A60B6AD7553BACA1 /* BlurHashDecode.swift */; }; B6EC2148FA5443C9289BEEBA /* MediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F17EFA1D3D09FC2F9C5E1CB2 /* MediaProvider.swift */; }; @@ -1556,7 +1555,6 @@ 4F75EF13F49DD2204E760910 /* FileRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRoomTimelineView.swift; sourceTree = ""; }; 4FA29BAE9B0F2D90E57B261C /* UserSessionFlowCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionFlowCoordinatorTests.swift; sourceTree = ""; }; 4FCB2126C091EEF2454B4D56 /* RoomFlowCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomFlowCoordinatorTests.swift; sourceTree = ""; }; - 4FD6E621CC5E6D4830D96D2D /* MockMediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMediaProvider.swift; sourceTree = ""; }; 4FDD775CFD72DD2D3C8A8390 /* NotificationSettingsProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsProxyProtocol.swift; sourceTree = ""; }; 502F986D57158674172C58E3 /* AppLockSetupSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupSettingsScreenModels.swift; sourceTree = ""; }; 505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModelTests.swift; sourceTree = ""; }; @@ -1680,6 +1678,7 @@ 6F1C3CBBC62C566DDF5E84C1 /* TimelineItemMenuAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemMenuAction.swift; sourceTree = ""; }; 6F3DFE5B444F131648066F05 /* StateStoreViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateStoreViewModel.swift; sourceTree = ""; }; 6F418426410F3823F3EB0828 /* WebRegistrationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRegistrationScreenViewModelProtocol.swift; sourceTree = ""; }; + 6F65E4BB9E82EB8373207CF8 /* MediaProviderMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProviderMock.swift; sourceTree = ""; }; 6F6E6EDC4BBF962B2ED595A4 /* MessageForwardingScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreenViewModelTests.swift; sourceTree = ""; }; 6FA38E813BE14149F173F461 /* PinnedEventsBannerStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedEventsBannerStateTests.swift; sourceTree = ""; }; 6FC5015B9634698BDB8701AF /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = it; path = it.lproj/Localizable.stringsdict; sourceTree = ""; }; @@ -2899,6 +2898,7 @@ E321E840DCC63790049984F4 /* ElementCallServiceMock.swift */, 1B10423B9102086A2D9BFCBA /* EventTimelineItem.swift */, 867DC9530C42F7B5176BE465 /* JoinedRoomProxyMock.swift */, + 6F65E4BB9E82EB8373207CF8 /* MediaProviderMock.swift */, 8DA1E8F287680C8ED25EDBAC /* NetworkMonitorMock.swift */, 382B50F7E379B3DBBD174364 /* NotificationSettingsProxyMock.swift */, B2AD8A56CD37E23071A2F4BF /* PHGPostHogMock.swift */, @@ -4977,7 +4977,6 @@ F17EFA1D3D09FC2F9C5E1CB2 /* MediaProvider.swift */, 85EB16E7FE59A947CA441531 /* MediaProviderProtocol.swift */, D49B9785E3AD7D1C15A29F2F /* MediaSourceProxy.swift */, - 4FD6E621CC5E6D4830D96D2D /* MockMediaProvider.swift */, ); path = Provider; sourceTree = ""; @@ -6035,7 +6034,6 @@ 9DD5AA10E85137140FEA86A3 /* MediaProvider.swift in Sources */, 7A642EE5F1ADC5D520F21924 /* MediaProviderProtocol.swift in Sources */, E2DB696117BAEABAD5718023 /* MediaSourceProxy.swift in Sources */, - 4FC085B1E5D1EB804495E2F4 /* MockMediaProvider.swift in Sources */, 5455147CAC63F71E48F7D699 /* NSELogger.swift in Sources */, 30CC4F796B27BE8B1DFDBF5A /* NSEUserSession.swift in Sources */, 1D5DC685CED904386C89B7DA /* NSRegularExpresion.swift in Sources */, @@ -6566,6 +6564,7 @@ F66BCCC825D6CA51724A94D0 /* MediaPlayerProvider.swift in Sources */, 762DAF94846C7AC8550F1CC1 /* MediaPlayerProviderProtocol.swift in Sources */, B6EC2148FA5443C9289BEEBA /* MediaProvider.swift in Sources */, + 478C363F63A5DFC3C83E334C /* MediaProviderMock.swift in Sources */, 30CC1DB7CE357659C82AA115 /* MediaProviderProtocol.swift in Sources */, 5897A59DDBD3592282092223 /* MediaSourceProxy.swift in Sources */, C67FCC854F3A6FC7A2EC04D0 /* MediaUploadPreviewScreen.swift in Sources */, @@ -6585,7 +6584,6 @@ F54E2D6CAD96E1AC15BC526F /* MessageForwardingScreenViewModel.swift in Sources */, C13128AAA787A4C2CBE4EE82 /* MessageForwardingScreenViewModelProtocol.swift in Sources */, C97325EFDCCEE457432A9E82 /* MessageText.swift in Sources */, - B659E3A49889E749E3239EA7 /* MockMediaProvider.swift in Sources */, 09C83DDDB07C28364F325209 /* MockRoomTimelineController.swift in Sources */, AF2ABA2794E376B64104C964 /* MockSoftLogoutScreenState.swift in Sources */, F9842667B68DC6FA1F9ECCBB /* NSItemProvider.swift in Sources */, diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 1cc39fdd50..356a7b892d 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -10199,6 +10199,429 @@ class MediaPlayerProviderMock: MediaPlayerProviderProtocol { await detachAllStatesExceptClosure?(exception) } } +class MediaProviderMock: MediaProviderProtocol { + + //MARK: - imageFromSource + + var imageFromSourceSizeUnderlyingCallsCount = 0 + var imageFromSourceSizeCallsCount: Int { + get { + if Thread.isMainThread { + return imageFromSourceSizeUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = imageFromSourceSizeUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + imageFromSourceSizeUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + imageFromSourceSizeUnderlyingCallsCount = newValue + } + } + } + } + var imageFromSourceSizeCalled: Bool { + return imageFromSourceSizeCallsCount > 0 + } + var imageFromSourceSizeReceivedArguments: (source: MediaSourceProxy?, size: CGSize?)? + var imageFromSourceSizeReceivedInvocations: [(source: MediaSourceProxy?, size: CGSize?)] = [] + + var imageFromSourceSizeUnderlyingReturnValue: UIImage? + var imageFromSourceSizeReturnValue: UIImage? { + get { + if Thread.isMainThread { + return imageFromSourceSizeUnderlyingReturnValue + } else { + var returnValue: UIImage?? = nil + DispatchQueue.main.sync { + returnValue = imageFromSourceSizeUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + imageFromSourceSizeUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + imageFromSourceSizeUnderlyingReturnValue = newValue + } + } + } + } + var imageFromSourceSizeClosure: ((MediaSourceProxy?, CGSize?) -> UIImage?)? + + func imageFromSource(_ source: MediaSourceProxy?, size: CGSize?) -> UIImage? { + imageFromSourceSizeCallsCount += 1 + imageFromSourceSizeReceivedArguments = (source: source, size: size) + DispatchQueue.main.async { + self.imageFromSourceSizeReceivedInvocations.append((source: source, size: size)) + } + if let imageFromSourceSizeClosure = imageFromSourceSizeClosure { + return imageFromSourceSizeClosure(source, size) + } else { + return imageFromSourceSizeReturnValue + } + } + //MARK: - loadImageFromSource + + var loadImageFromSourceSizeUnderlyingCallsCount = 0 + var loadImageFromSourceSizeCallsCount: Int { + get { + if Thread.isMainThread { + return loadImageFromSourceSizeUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = loadImageFromSourceSizeUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + loadImageFromSourceSizeUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + loadImageFromSourceSizeUnderlyingCallsCount = newValue + } + } + } + } + var loadImageFromSourceSizeCalled: Bool { + return loadImageFromSourceSizeCallsCount > 0 + } + var loadImageFromSourceSizeReceivedArguments: (source: MediaSourceProxy, size: CGSize?)? + var loadImageFromSourceSizeReceivedInvocations: [(source: MediaSourceProxy, size: CGSize?)] = [] + + var loadImageFromSourceSizeUnderlyingReturnValue: Result! + var loadImageFromSourceSizeReturnValue: Result! { + get { + if Thread.isMainThread { + return loadImageFromSourceSizeUnderlyingReturnValue + } else { + var returnValue: Result? = nil + DispatchQueue.main.sync { + returnValue = loadImageFromSourceSizeUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + loadImageFromSourceSizeUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + loadImageFromSourceSizeUnderlyingReturnValue = newValue + } + } + } + } + var loadImageFromSourceSizeClosure: ((MediaSourceProxy, CGSize?) async -> Result)? + + func loadImageFromSource(_ source: MediaSourceProxy, size: CGSize?) async -> Result { + loadImageFromSourceSizeCallsCount += 1 + loadImageFromSourceSizeReceivedArguments = (source: source, size: size) + DispatchQueue.main.async { + self.loadImageFromSourceSizeReceivedInvocations.append((source: source, size: size)) + } + if let loadImageFromSourceSizeClosure = loadImageFromSourceSizeClosure { + return await loadImageFromSourceSizeClosure(source, size) + } else { + return loadImageFromSourceSizeReturnValue + } + } + //MARK: - loadImageDataFromSource + + var loadImageDataFromSourceUnderlyingCallsCount = 0 + var loadImageDataFromSourceCallsCount: Int { + get { + if Thread.isMainThread { + return loadImageDataFromSourceUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = loadImageDataFromSourceUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + loadImageDataFromSourceUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + loadImageDataFromSourceUnderlyingCallsCount = newValue + } + } + } + } + var loadImageDataFromSourceCalled: Bool { + return loadImageDataFromSourceCallsCount > 0 + } + var loadImageDataFromSourceReceivedSource: MediaSourceProxy? + var loadImageDataFromSourceReceivedInvocations: [MediaSourceProxy] = [] + + var loadImageDataFromSourceUnderlyingReturnValue: Result! + var loadImageDataFromSourceReturnValue: Result! { + get { + if Thread.isMainThread { + return loadImageDataFromSourceUnderlyingReturnValue + } else { + var returnValue: Result? = nil + DispatchQueue.main.sync { + returnValue = loadImageDataFromSourceUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + loadImageDataFromSourceUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + loadImageDataFromSourceUnderlyingReturnValue = newValue + } + } + } + } + var loadImageDataFromSourceClosure: ((MediaSourceProxy) async -> Result)? + + func loadImageDataFromSource(_ source: MediaSourceProxy) async -> Result { + loadImageDataFromSourceCallsCount += 1 + loadImageDataFromSourceReceivedSource = source + DispatchQueue.main.async { + self.loadImageDataFromSourceReceivedInvocations.append(source) + } + if let loadImageDataFromSourceClosure = loadImageDataFromSourceClosure { + return await loadImageDataFromSourceClosure(source) + } else { + return loadImageDataFromSourceReturnValue + } + } + //MARK: - loadImageRetryingOnReconnection + + var loadImageRetryingOnReconnectionSizeUnderlyingCallsCount = 0 + var loadImageRetryingOnReconnectionSizeCallsCount: Int { + get { + if Thread.isMainThread { + return loadImageRetryingOnReconnectionSizeUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = loadImageRetryingOnReconnectionSizeUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + loadImageRetryingOnReconnectionSizeUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + loadImageRetryingOnReconnectionSizeUnderlyingCallsCount = newValue + } + } + } + } + var loadImageRetryingOnReconnectionSizeCalled: Bool { + return loadImageRetryingOnReconnectionSizeCallsCount > 0 + } + var loadImageRetryingOnReconnectionSizeReceivedArguments: (source: MediaSourceProxy, size: CGSize?)? + var loadImageRetryingOnReconnectionSizeReceivedInvocations: [(source: MediaSourceProxy, size: CGSize?)] = [] + + var loadImageRetryingOnReconnectionSizeUnderlyingReturnValue: Task! + var loadImageRetryingOnReconnectionSizeReturnValue: Task! { + get { + if Thread.isMainThread { + return loadImageRetryingOnReconnectionSizeUnderlyingReturnValue + } else { + var returnValue: Task? = nil + DispatchQueue.main.sync { + returnValue = loadImageRetryingOnReconnectionSizeUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + loadImageRetryingOnReconnectionSizeUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + loadImageRetryingOnReconnectionSizeUnderlyingReturnValue = newValue + } + } + } + } + var loadImageRetryingOnReconnectionSizeClosure: ((MediaSourceProxy, CGSize?) -> Task)? + + func loadImageRetryingOnReconnection(_ source: MediaSourceProxy, size: CGSize?) -> Task { + loadImageRetryingOnReconnectionSizeCallsCount += 1 + loadImageRetryingOnReconnectionSizeReceivedArguments = (source: source, size: size) + DispatchQueue.main.async { + self.loadImageRetryingOnReconnectionSizeReceivedInvocations.append((source: source, size: size)) + } + if let loadImageRetryingOnReconnectionSizeClosure = loadImageRetryingOnReconnectionSizeClosure { + return loadImageRetryingOnReconnectionSizeClosure(source, size) + } else { + return loadImageRetryingOnReconnectionSizeReturnValue + } + } + //MARK: - loadThumbnailForSource + + var loadThumbnailForSourceSourceSizeUnderlyingCallsCount = 0 + var loadThumbnailForSourceSourceSizeCallsCount: Int { + get { + if Thread.isMainThread { + return loadThumbnailForSourceSourceSizeUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = loadThumbnailForSourceSourceSizeUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + loadThumbnailForSourceSourceSizeUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + loadThumbnailForSourceSourceSizeUnderlyingCallsCount = newValue + } + } + } + } + var loadThumbnailForSourceSourceSizeCalled: Bool { + return loadThumbnailForSourceSourceSizeCallsCount > 0 + } + var loadThumbnailForSourceSourceSizeReceivedArguments: (source: MediaSourceProxy, size: CGSize)? + var loadThumbnailForSourceSourceSizeReceivedInvocations: [(source: MediaSourceProxy, size: CGSize)] = [] + + var loadThumbnailForSourceSourceSizeUnderlyingReturnValue: Result! + var loadThumbnailForSourceSourceSizeReturnValue: Result! { + get { + if Thread.isMainThread { + return loadThumbnailForSourceSourceSizeUnderlyingReturnValue + } else { + var returnValue: Result? = nil + DispatchQueue.main.sync { + returnValue = loadThumbnailForSourceSourceSizeUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + loadThumbnailForSourceSourceSizeUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + loadThumbnailForSourceSourceSizeUnderlyingReturnValue = newValue + } + } + } + } + var loadThumbnailForSourceSourceSizeClosure: ((MediaSourceProxy, CGSize) async -> Result)? + + func loadThumbnailForSource(source: MediaSourceProxy, size: CGSize) async -> Result { + loadThumbnailForSourceSourceSizeCallsCount += 1 + loadThumbnailForSourceSourceSizeReceivedArguments = (source: source, size: size) + DispatchQueue.main.async { + self.loadThumbnailForSourceSourceSizeReceivedInvocations.append((source: source, size: size)) + } + if let loadThumbnailForSourceSourceSizeClosure = loadThumbnailForSourceSourceSizeClosure { + return await loadThumbnailForSourceSourceSizeClosure(source, size) + } else { + return loadThumbnailForSourceSourceSizeReturnValue + } + } + //MARK: - loadFileFromSource + + var loadFileFromSourceBodyUnderlyingCallsCount = 0 + var loadFileFromSourceBodyCallsCount: Int { + get { + if Thread.isMainThread { + return loadFileFromSourceBodyUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = loadFileFromSourceBodyUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + loadFileFromSourceBodyUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + loadFileFromSourceBodyUnderlyingCallsCount = newValue + } + } + } + } + var loadFileFromSourceBodyCalled: Bool { + return loadFileFromSourceBodyCallsCount > 0 + } + var loadFileFromSourceBodyReceivedArguments: (source: MediaSourceProxy, body: String?)? + var loadFileFromSourceBodyReceivedInvocations: [(source: MediaSourceProxy, body: String?)] = [] + + var loadFileFromSourceBodyUnderlyingReturnValue: Result! + var loadFileFromSourceBodyReturnValue: Result! { + get { + if Thread.isMainThread { + return loadFileFromSourceBodyUnderlyingReturnValue + } else { + var returnValue: Result? = nil + DispatchQueue.main.sync { + returnValue = loadFileFromSourceBodyUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + loadFileFromSourceBodyUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + loadFileFromSourceBodyUnderlyingReturnValue = newValue + } + } + } + } + var loadFileFromSourceBodyClosure: ((MediaSourceProxy, String?) async -> Result)? + + func loadFileFromSource(_ source: MediaSourceProxy, body: String?) async -> Result { + loadFileFromSourceBodyCallsCount += 1 + loadFileFromSourceBodyReceivedArguments = (source: source, body: body) + DispatchQueue.main.async { + self.loadFileFromSourceBodyReceivedInvocations.append((source: source, body: body)) + } + if let loadFileFromSourceBodyClosure = loadFileFromSourceBodyClosure { + return await loadFileFromSourceBodyClosure(source, body) + } else { + return loadFileFromSourceBodyReturnValue + } + } +} class NetworkMonitorMock: NetworkMonitorProtocol { var reachabilityPublisher: CurrentValuePublisher { get { return underlyingReachabilityPublisher } diff --git a/ElementX/Sources/Mocks/MediaProviderMock.swift b/ElementX/Sources/Mocks/MediaProviderMock.swift new file mode 100644 index 0000000000..da7a26909f --- /dev/null +++ b/ElementX/Sources/Mocks/MediaProviderMock.swift @@ -0,0 +1,57 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import SwiftUI + +extension MediaProviderMock { + struct Configuration { } + + convenience init(configuration: Configuration) { + self.init() + + imageFromSourceSizeClosure = { mediaSource, _ in + guard mediaSource != nil else { + return nil + } + + if mediaSource?.url == .picturesDirectory { + return Asset.Images.appLogo.image + } + + return UIImage(systemName: "photo") + } + + loadImageFromSourceSizeClosure = { _, _ in + guard let image = UIImage(systemName: "photo") else { + fatalError() + } + + return .success(image) + } + + loadImageDataFromSourceClosure = { _ in + guard let image = UIImage(systemName: "photo"), + let data = image.pngData() else { + fatalError() + } + + return .success(data) + } + + loadFileFromSourceBodyReturnValue = .failure(.failedRetrievingFile) + + loadImageRetryingOnReconnectionSizeClosure = { _, _ in + Task { + guard let image = UIImage(systemName: "photo") else { + fatalError() + } + + return image + } + } + } +} diff --git a/ElementX/Sources/Mocks/UserSessionMock.swift b/ElementX/Sources/Mocks/UserSessionMock.swift index 059b0b07f2..f9d0f510bb 100644 --- a/ElementX/Sources/Mocks/UserSessionMock.swift +++ b/ElementX/Sources/Mocks/UserSessionMock.swift @@ -17,7 +17,7 @@ extension UserSessionMock { self.init() clientProxy = configuration.clientProxy - mediaProvider = MockMediaProvider() + mediaProvider = MediaProviderMock(configuration: .init()) voiceMessageMediaManager = VoiceMessageMediaManagerMock() sessionSecurityStatePublisher = CurrentValueSubject(.init(verificationState: .verified, recoveryState: .enabled)).asCurrentValuePublisher() diff --git a/ElementX/Sources/Other/Pills/PillAttachmentViewProvider.swift b/ElementX/Sources/Other/Pills/PillAttachmentViewProvider.swift index 2e96579881..bfa883033c 100644 --- a/ElementX/Sources/Other/Pills/PillAttachmentViewProvider.swift +++ b/ElementX/Sources/Other/Pills/PillAttachmentViewProvider.swift @@ -46,7 +46,7 @@ final class PillAttachmentViewProvider: NSTextAttachmentViewProvider, NSSecureCo if ProcessInfo.isXcodePreview || ProcessInfo.isRunningTests { // The mock viewModel simulates the loading logic for testing purposes context = PillContext.mock(type: .loadUser(isOwn: false)) - mediaProvider = MockMediaProvider() + mediaProvider = MediaProviderMock(configuration: .init()) } else if let timelineContext = delegate?.timelineContext { context = PillContext(timelineContext: timelineContext, data: pillData) mediaProvider = timelineContext.mediaProvider diff --git a/ElementX/Sources/Other/Pills/PillView.swift b/ElementX/Sources/Other/Pills/PillView.swift index 91265de629..2a0deee763 100644 --- a/ElementX/Sources/Other/Pills/PillView.swift +++ b/ElementX/Sources/Other/Pills/PillView.swift @@ -37,7 +37,7 @@ struct PillView: View { } struct PillView_Previews: PreviewProvider, TestablePreview { - static let mockMediaProvider = MockMediaProvider() + static let mockMediaProvider = MediaProviderMock(configuration: .init()) static var previews: some View { PillView(mediaProvider: mockMediaProvider, diff --git a/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift b/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift index 4705b76a13..26d8443bf6 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift @@ -189,7 +189,7 @@ struct AvatarHeaderView_Previews: PreviewProvider, TestablePreview { isEncrypted: true, isPublic: true), avatarSize: .room(on: .details), - mediaProvider: MockMediaProvider()) { + mediaProvider: MediaProviderMock(configuration: .init())) { HStack(spacing: 32) { ShareLink(item: "test") { CompoundIcon(\.shareIos) @@ -203,7 +203,7 @@ struct AvatarHeaderView_Previews: PreviewProvider, TestablePreview { Form { AvatarHeaderView(accountOwner: RoomMemberDetails(withProxy: RoomMemberProxyMock.mockMe), dmRecipient: RoomMemberDetails(withProxy: RoomMemberProxyMock.mockAlice), - mediaProvider: MockMediaProvider()) { + mediaProvider: MediaProviderMock(configuration: .init())) { HStack(spacing: 32) { ShareLink(item: "test") { CompoundIcon(\.shareIos) @@ -218,11 +218,11 @@ struct AvatarHeaderView_Previews: PreviewProvider, TestablePreview { VStack(spacing: 16) { AvatarHeaderView(member: RoomMemberDetails(withProxy: RoomMemberProxyMock.mockAlice), avatarSize: .room(on: .details), - mediaProvider: MockMediaProvider()) { Text("") } + mediaProvider: MediaProviderMock(configuration: .init())) { Text("") } AvatarHeaderView(member: RoomMemberDetails(withProxy: RoomMemberProxyMock.mockBanned[3]), avatarSize: .room(on: .details), - mediaProvider: MockMediaProvider()) { Text("") } + mediaProvider: MediaProviderMock(configuration: .init())) { Text("") } } .padding() .background(Color.compound.bgSubtleSecondaryLevel0) diff --git a/ElementX/Sources/Other/SwiftUI/Views/RoomAvatarImage.swift b/ElementX/Sources/Other/SwiftUI/Views/RoomAvatarImage.swift index 78d4c96e1b..17cf55e783 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/RoomAvatarImage.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/RoomAvatarImage.swift @@ -93,30 +93,30 @@ struct RoomAvatarImage_Previews: PreviewProvider, TestablePreview { name: "Room", avatarURL: nil), avatarSize: .room(on: .home), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) RoomAvatarImage(avatar: .room(id: "!2:server.com", name: "Room", avatarURL: .picturesDirectory), avatarSize: .room(on: .home), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) RoomAvatarImage(avatar: .heroes([.init(userID: "@user:server.com", displayName: "User", avatarURL: nil)]), avatarSize: .room(on: .home), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) RoomAvatarImage(avatar: .heroes([.init(userID: "@user:server.com", displayName: "User", avatarURL: .picturesDirectory)]), avatarSize: .room(on: .home), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) RoomAvatarImage(avatar: .heroes([.init(userID: "@alice:server.com", displayName: "Alice", avatarURL: nil), .init(userID: "@bob:server.net", displayName: "Bob", avatarURL: nil)]), avatarSize: .room(on: .home), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) } } } diff --git a/ElementX/Sources/Other/SwiftUI/Views/RoomInviterLabel.swift b/ElementX/Sources/Other/SwiftUI/Views/RoomInviterLabel.swift index e3fa721ab1..2d7a71eb8f 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/RoomInviterLabel.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/RoomInviterLabel.swift @@ -62,13 +62,13 @@ struct RoomInviterLabel_Previews: PreviewProvider, TestablePreview { static var previews: some View { VStack(spacing: 10) { RoomInviterLabel(inviter: .init(member: RoomMemberProxyMock.mockAlice), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) RoomInviterLabel(inviter: .init(member: RoomMemberProxyMock.mockDan), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) RoomInviterLabel(inviter: .init(member: RoomMemberProxyMock.mockNoName), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) RoomInviterLabel(inviter: .init(member: RoomMemberProxyMock.mockCharlie), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) .foregroundStyle(.compound.textPrimary) } .font(.compound.bodyMD) diff --git a/ElementX/Sources/Other/SwiftUI/Views/UserProfileListRow.swift b/ElementX/Sources/Other/SwiftUI/Views/UserProfileListRow.swift index 762fbce485..4d43bfbd16 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/UserProfileListRow.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/UserProfileListRow.swift @@ -65,21 +65,21 @@ struct UserProfileCell_Previews: PreviewProvider, TestablePreview { static var previews: some View { Form { - UserProfileListRow(user: .mockAlice, membership: nil, mediaProvider: MockMediaProvider(), + UserProfileListRow(user: .mockAlice, membership: nil, mediaProvider: MediaProviderMock(configuration: .init()), kind: .multiSelection(isSelected: true, action: action)) - UserProfileListRow(user: .mockBob, membership: nil, mediaProvider: MockMediaProvider(), + UserProfileListRow(user: .mockBob, membership: nil, mediaProvider: MediaProviderMock(configuration: .init()), kind: .multiSelection(isSelected: false, action: action)) - UserProfileListRow(user: .mockCharlie, membership: .join, mediaProvider: MockMediaProvider(), + UserProfileListRow(user: .mockCharlie, membership: .join, mediaProvider: MediaProviderMock(configuration: .init()), kind: .multiSelection(isSelected: true, action: action)) .disabled(true) - UserProfileListRow(user: .init(userID: "@someone:matrix.org"), membership: .join, mediaProvider: MockMediaProvider(), + UserProfileListRow(user: .init(userID: "@someone:matrix.org"), membership: .join, mediaProvider: MediaProviderMock(configuration: .init()), kind: .multiSelection(isSelected: false, action: action)) .disabled(true) - UserProfileListRow(user: .init(userID: "@someone:matrix.org"), membership: nil, mediaProvider: MockMediaProvider(), + UserProfileListRow(user: .init(userID: "@someone:matrix.org"), membership: nil, mediaProvider: MediaProviderMock(configuration: .init()), kind: .multiSelection(isSelected: false, action: action)) } .compoundList() diff --git a/ElementX/Sources/Screens/BlockedUsersScreen/View/BlockedUsersScreen.swift b/ElementX/Sources/Screens/BlockedUsersScreen/View/BlockedUsersScreen.swift index 977ae74a15..1922e1f92b 100644 --- a/ElementX/Sources/Screens/BlockedUsersScreen/View/BlockedUsersScreen.swift +++ b/ElementX/Sources/Screens/BlockedUsersScreen/View/BlockedUsersScreen.swift @@ -55,7 +55,7 @@ struct BlockedUsersScreen: View { struct BlockedUsersScreen_Previews: PreviewProvider, TestablePreview { static let viewModel = BlockedUsersScreenViewModel(hideProfiles: true, clientProxy: ClientProxyMock(.init(userID: RoomMemberProxyMock.mockMe.userID)), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: UserIndicatorControllerMock()) static var previews: some View { diff --git a/ElementX/Sources/Screens/GlobalSearchScreen/View/GlobalSearchScreen.swift b/ElementX/Sources/Screens/GlobalSearchScreen/View/GlobalSearchScreen.swift index 25fbd5a2c8..388ee539ae 100644 --- a/ElementX/Sources/Screens/GlobalSearchScreen/View/GlobalSearchScreen.swift +++ b/ElementX/Sources/Screens/GlobalSearchScreen/View/GlobalSearchScreen.swift @@ -206,7 +206,7 @@ private class GlobalSearchTextField: UITextField { struct GlobalSearchScreen_Previews: PreviewProvider, TestablePreview { static let viewModel = GlobalSearchScreenViewModel(roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) static var previews: some View { NavigationStack { diff --git a/ElementX/Sources/Screens/GlobalSearchScreen/View/GlobalSearchScreenCell.swift b/ElementX/Sources/Screens/GlobalSearchScreen/View/GlobalSearchScreenCell.swift index c298a5353a..6a9225facd 100644 --- a/ElementX/Sources/Screens/GlobalSearchScreen/View/GlobalSearchScreenCell.swift +++ b/ElementX/Sources/Screens/GlobalSearchScreen/View/GlobalSearchScreenCell.swift @@ -37,7 +37,7 @@ struct GlobalSearchScreenListRow: View { struct GlobalSearchScreenListRow_Previews: PreviewProvider, TestablePreview { static let viewModel = GlobalSearchScreenViewModel(roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) static var previews: some View { List { diff --git a/ElementX/Sources/Screens/InviteUsersScreen/View/InviteUsersScreen.swift b/ElementX/Sources/Screens/InviteUsersScreen/View/InviteUsersScreen.swift index 067eb290c7..d677031815 100644 --- a/ElementX/Sources/Screens/InviteUsersScreen/View/InviteUsersScreen.swift +++ b/ElementX/Sources/Screens/InviteUsersScreen/View/InviteUsersScreen.swift @@ -154,7 +154,7 @@ struct InviteUsersScreen_Previews: PreviewProvider, TestablePreview { return InviteUsersScreenViewModel(clientProxy: ClientProxyMock(.init()), selectedUsers: .init([]), roomType: .draft, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userDiscoveryService: userDiscoveryService, userIndicatorController: UserIndicatorControllerMock()) }() diff --git a/ElementX/Sources/Screens/InviteUsersScreen/View/InviteUsersScreenSelectedItem.swift b/ElementX/Sources/Screens/InviteUsersScreen/View/InviteUsersScreenSelectedItem.swift index 3f970c812a..5316cd9763 100644 --- a/ElementX/Sources/Screens/InviteUsersScreen/View/InviteUsersScreenSelectedItem.swift +++ b/ElementX/Sources/Screens/InviteUsersScreen/View/InviteUsersScreenSelectedItem.swift @@ -50,7 +50,7 @@ struct InviteUsersScreenSelectedItem_Previews: PreviewProvider, TestablePreview ScrollView(.horizontal) { HStack(spacing: 28) { ForEach(people, id: \.userID) { user in - InviteUsersScreenSelectedItem(user: user, mediaProvider: MockMediaProvider(), dismissAction: { }) + InviteUsersScreenSelectedItem(user: user, mediaProvider: MediaProviderMock(configuration: .init()), dismissAction: { }) .frame(width: 72) } } diff --git a/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift b/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift index 6fa7093b6e..7fe3f317f5 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift @@ -168,7 +168,7 @@ struct JoinRoomScreen_Previews: PreviewProvider, TestablePreview { via: [], allowKnocking: true, clientProxy: clientProxy, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController) } } diff --git a/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift b/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift index 5975bfabab..8dc876e85b 100644 --- a/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift +++ b/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift @@ -99,7 +99,7 @@ struct MessageForwardingScreen_Previews: PreviewProvider, TestablePreview { clientProxy: ClientProxyMock(.init()), roomSummaryProvider: summaryProvider, userIndicatorController: UserIndicatorControllerMock(), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) NavigationStack { MessageForwardingScreen(context: viewModel.context) diff --git a/ElementX/Sources/Screens/PinnedEventsTimelineScreen/View/PinnedEventsTimelineScreen.swift b/ElementX/Sources/Screens/PinnedEventsTimelineScreen/View/PinnedEventsTimelineScreen.swift index 8e7f047115..5e8b75eaa2 100644 --- a/ElementX/Sources/Screens/PinnedEventsTimelineScreen/View/PinnedEventsTimelineScreen.swift +++ b/ElementX/Sources/Screens/PinnedEventsTimelineScreen/View/PinnedEventsTimelineScreen.swift @@ -90,7 +90,7 @@ struct PinnedEventsTimelineScreen_Previews: PreviewProvider, TestablePreview { timelineController.timelineItems = [] return TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")), timelineController: timelineController, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: UserIndicatorControllerMock(), diff --git a/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreen.swift b/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreen.swift index 54c328e73f..02975c9bcc 100644 --- a/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreen.swift +++ b/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreen.swift @@ -122,7 +122,7 @@ struct RoomChangeRolesScreen_Previews: PreviewProvider, TestablePreview { static func makeViewModel(mode: RoomMemberDetails.Role) -> RoomChangeRolesScreenViewModel { RoomChangeRolesScreenViewModel(mode: mode, roomProxy: JoinedRoomProxyMock(.init(members: .allMembersAsAdmin)), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: UserIndicatorControllerMock(), analytics: ServiceLocator.shared.analytics) } diff --git a/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreenRow.swift b/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreenRow.swift index d9cec3331e..e0db0f7510 100644 --- a/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreenRow.swift +++ b/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreenRow.swift @@ -39,34 +39,34 @@ struct RoomChangeRolesScreenRow_Previews: PreviewProvider, TestablePreview { static var previews: some View { Form { RoomChangeRolesScreenRow(member: .init(withProxy: RoomMemberProxyMock.mockAlice), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), isSelected: true, action: action) RoomChangeRolesScreenRow(member: .init(withProxy: RoomMemberProxyMock.mockBob), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), isSelected: false, action: action) RoomChangeRolesScreenRow(member: .init(withProxy: RoomMemberProxyMock.mockInvited), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), isSelected: false, action: action) RoomChangeRolesScreenRow(member: .init(withProxy: RoomMemberProxyMock.mockCharlie), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), isSelected: true, action: action) .disabled(true) RoomChangeRolesScreenRow(member: .init(withProxy: RoomMemberProxyMock(with: .init(userID: "@someone:matrix.org", membership: .join))), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), isSelected: false, action: action) .disabled(true) RoomChangeRolesScreenRow(member: .init(withProxy: RoomMemberProxyMock(with: .init(userID: "@someone:matrix.org", membership: .join))), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), isSelected: false, action: action) } diff --git a/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreenSelectedItem.swift b/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreenSelectedItem.swift index dbbe1d5d5b..435fd33a97 100644 --- a/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreenSelectedItem.swift +++ b/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreenSelectedItem.swift @@ -60,7 +60,7 @@ struct RoomChangeRolesScreenSelectedItem_Previews: PreviewProvider, TestablePrev HStack(spacing: 12) { ForEach(members, id: \.id) { member in RoomChangeRolesScreenSelectedItem(member: member, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), dismissAction: { }) .frame(width: 72) } diff --git a/ElementX/Sources/Screens/RoomDetailsEditScreen/View/RoomDetailsEditScreen.swift b/ElementX/Sources/Screens/RoomDetailsEditScreen/View/RoomDetailsEditScreen.swift index db4a1c6abc..3f4cd5fd35 100644 --- a/ElementX/Sources/Screens/RoomDetailsEditScreen/View/RoomDetailsEditScreen.swift +++ b/ElementX/Sources/Screens/RoomDetailsEditScreen/View/RoomDetailsEditScreen.swift @@ -153,7 +153,7 @@ struct RoomDetailsEditScreen_Previews: PreviewProvider, TestablePreview { members: [.mockMeAdmin])) return RoomDetailsEditScreenViewModel(roomProxy: roomProxy, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: UserIndicatorControllerMock.default) }() @@ -163,7 +163,7 @@ struct RoomDetailsEditScreen_Previews: PreviewProvider, TestablePreview { members: [.mockAlice])) return RoomDetailsEditScreenViewModel(roomProxy: roomProxy, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: UserIndicatorControllerMock.default) }() diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift b/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift index 919aa3aff8..1126194ab4 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift @@ -321,7 +321,7 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview { return RoomDetailsScreenViewModel(roomProxy: roomProxy, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: notificationSettingsProxy, @@ -347,7 +347,7 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview { return RoomDetailsScreenViewModel(roomProxy: roomProxy, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: notificationSettingsProxy, @@ -372,7 +372,7 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview { return RoomDetailsScreenViewModel(roomProxy: roomProxy, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: notificationSettingsProxy, diff --git a/ElementX/Sources/Screens/RoomDirectorySearchScreen/View/RoomDirectoryCell.swift b/ElementX/Sources/Screens/RoomDirectorySearchScreen/View/RoomDirectoryCell.swift index fab1dd568b..a1c423554a 100644 --- a/ElementX/Sources/Screens/RoomDirectorySearchScreen/View/RoomDirectoryCell.swift +++ b/ElementX/Sources/Screens/RoomDirectorySearchScreen/View/RoomDirectoryCell.swift @@ -55,7 +55,7 @@ struct RoomDirectorySearchCell_Previews: PreviewProvider, TestablePreview { name: "Test title", avatarURL: nil), canBeJoined: true), - mediaProvider: MockMediaProvider()) { } + mediaProvider: MediaProviderMock(configuration: .init())) { } RoomDirectorySearchCell(result: .init(id: "!test_id_2:matrix.org", alias: "#test:example.com", @@ -65,7 +65,7 @@ struct RoomDirectorySearchCell_Previews: PreviewProvider, TestablePreview { name: nil, avatarURL: nil), canBeJoined: true), - mediaProvider: MockMediaProvider()) { } + mediaProvider: MediaProviderMock(configuration: .init())) { } RoomDirectorySearchCell(result: .init(id: "!test_id_3:example.com", alias: "#test_no_topic:example.com", @@ -75,7 +75,7 @@ struct RoomDirectorySearchCell_Previews: PreviewProvider, TestablePreview { name: "Test title no topic", avatarURL: nil), canBeJoined: true), - mediaProvider: MockMediaProvider()) { } + mediaProvider: MediaProviderMock(configuration: .init())) { } RoomDirectorySearchCell(result: .init(id: "!test_id_4:example.com", alias: "#test_no_topic:example.com", @@ -85,7 +85,7 @@ struct RoomDirectorySearchCell_Previews: PreviewProvider, TestablePreview { name: nil, avatarURL: nil), canBeJoined: true), - mediaProvider: MockMediaProvider()) { } + mediaProvider: MediaProviderMock(configuration: .init())) { } RoomDirectorySearchCell(result: .init(id: "!test_id_5:example.com", alias: nil, @@ -95,7 +95,7 @@ struct RoomDirectorySearchCell_Previews: PreviewProvider, TestablePreview { name: "Test title no alias", avatarURL: nil), canBeJoined: false), - mediaProvider: MockMediaProvider()) { } + mediaProvider: MediaProviderMock(configuration: .init())) { } RoomDirectorySearchCell(result: .init(id: "!test_id_6:example.com", alias: nil, @@ -105,7 +105,7 @@ struct RoomDirectorySearchCell_Previews: PreviewProvider, TestablePreview { name: "Test title no alias", avatarURL: nil), canBeJoined: false), - mediaProvider: MockMediaProvider()) { } + mediaProvider: MediaProviderMock(configuration: .init())) { } RoomDirectorySearchCell(result: .init(id: "!test_id_7:example.com", alias: nil, @@ -115,7 +115,7 @@ struct RoomDirectorySearchCell_Previews: PreviewProvider, TestablePreview { name: nil, avatarURL: nil), canBeJoined: false), - mediaProvider: MockMediaProvider()) { } + mediaProvider: MediaProviderMock(configuration: .init())) { } RoomDirectorySearchCell(result: .init(id: "!test_id_8:example.com", alias: nil, name: nil, @@ -124,7 +124,7 @@ struct RoomDirectorySearchCell_Previews: PreviewProvider, TestablePreview { name: nil, avatarURL: nil), canBeJoined: false), - mediaProvider: MockMediaProvider()) { } + mediaProvider: MediaProviderMock(configuration: .init())) { } } .compoundList() } diff --git a/ElementX/Sources/Screens/RoomDirectorySearchScreen/View/RoomDirectorySearchScreen.swift b/ElementX/Sources/Screens/RoomDirectorySearchScreen/View/RoomDirectorySearchScreen.swift index 779a312cf7..95a67e0305 100644 --- a/ElementX/Sources/Screens/RoomDirectorySearchScreen/View/RoomDirectorySearchScreen.swift +++ b/ElementX/Sources/Screens/RoomDirectorySearchScreen/View/RoomDirectorySearchScreen.swift @@ -93,7 +93,7 @@ struct RoomDirectorySearchScreen_Previews: PreviewProvider, TestablePreview { return RoomDirectorySearchScreenViewModel(clientProxy: clientProxy, userIndicatorController: UserIndicatorControllerMock(), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) }() static var previews: some View { diff --git a/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift b/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift index 358b19d968..680aea69e5 100644 --- a/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift +++ b/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift @@ -147,7 +147,7 @@ struct RoomMemberDetailsScreen_Previews: PreviewProvider, TestablePreview { return RoomMemberDetailsScreenViewModel(userID: member.userID, roomProxy: roomProxyMock, clientProxy: clientProxyMock, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController, analytics: ServiceLocator.shared.analytics) } diff --git a/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListManageMemberSheet.swift b/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListManageMemberSheet.swift index ca99f99108..06d007a59e 100644 --- a/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListManageMemberSheet.swift +++ b/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListManageMemberSheet.swift @@ -100,7 +100,7 @@ private extension RoomMembersListScreenViewModel { static var mock: RoomMembersListScreenViewModel { RoomMembersListScreenViewModel(initialMode: .members, roomProxy: JoinedRoomProxyMock(.init(members: .allMembersAsAdmin)), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController, analytics: ServiceLocator.shared.analytics) } diff --git a/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreen.swift b/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreen.swift index ade0098d71..a5ac031b32 100644 --- a/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreen.swift +++ b/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreen.swift @@ -174,7 +174,7 @@ struct RoomMembersListScreen_Previews: PreviewProvider, TestablePreview { members: members, ownUserID: ownUserID, canUserInvite: false)), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController, analytics: ServiceLocator.shared.analytics) } diff --git a/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreenMemberCell.swift b/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreenMemberCell.swift index f84e177565..efd25c1706 100644 --- a/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreenMemberCell.swift +++ b/ElementX/Sources/Screens/RoomMemberListScreen/View/RoomMembersListScreenMemberCell.swift @@ -99,7 +99,7 @@ struct RoomMembersListMemberCell_Previews: PreviewProvider, TestablePreview { static let viewModel = RoomMembersListScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Some room", members: members)), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController, analytics: ServiceLocator.shared.analytics) static var previews: some View { diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/CompletionSuggestionView.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/CompletionSuggestionView.swift index fcb0536b98..29b5ff4cc9 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/CompletionSuggestionView.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/CompletionSuggestionView.swift @@ -116,12 +116,12 @@ struct CompletionSuggestion_Previews: PreviewProvider, TestablePreview { static var previews: some View { // Putting them is VStack allows the preview to work properly in tests VStack(spacing: 8) { - CompletionSuggestionView(mediaProvider: MockMediaProvider(), + CompletionSuggestionView(mediaProvider: MediaProviderMock(configuration: .init()), items: [.user(item: MentionSuggestionItem(id: "@user_mention_1:matrix.org", displayName: "User 1", avatarURL: nil, range: .init())), .user(item: MentionSuggestionItem(id: "@user_mention_2:matrix.org", displayName: "User 2", avatarURL: URL.documentsDirectory, range: .init()))]) { _ in } } VStack(spacing: 8) { - CompletionSuggestionView(mediaProvider: MockMediaProvider(), + CompletionSuggestionView(mediaProvider: MediaProviderMock(configuration: .init()), items: multipleItems) { _ in } } } diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift index 0c463ad1e2..c4f4fa384f 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift @@ -295,7 +295,7 @@ struct ComposerToolbar_Previews: PreviewProvider, TestablePreview { static let wysiwygViewModel = WysiwygComposerViewModel() static let composerViewModel = ComposerToolbarViewModel(wysiwygViewModel: wysiwygViewModel, completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init(suggestions: suggestions)), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) @@ -338,7 +338,7 @@ extension ComposerToolbar { var composerViewModel: ComposerToolbarViewModel { let model = ComposerToolbarViewModel(wysiwygViewModel: wysiwygViewModel, completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) @@ -355,7 +355,7 @@ extension ComposerToolbar { var composerViewModel: ComposerToolbarViewModel { let model = ComposerToolbarViewModel(wysiwygViewModel: wysiwygViewModel, completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) @@ -372,7 +372,7 @@ extension ComposerToolbar { var composerViewModel: ComposerToolbarViewModel { let model = ComposerToolbarViewModel(wysiwygViewModel: wysiwygViewModel, completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) @@ -390,7 +390,7 @@ extension ComposerToolbar { var composerViewModel: ComposerToolbarViewModel { let model = ComposerToolbarViewModel(wysiwygViewModel: wysiwygViewModel, completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) @@ -411,7 +411,7 @@ extension ComposerToolbar { var composerViewModel: ComposerToolbarViewModel { let model = ComposerToolbarViewModel(wysiwygViewModel: wysiwygViewModel, completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MentionSuggestionItemView.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MentionSuggestionItemView.swift index 41e29b414d..eea817a9e6 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MentionSuggestionItemView.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MentionSuggestionItemView.swift @@ -35,7 +35,7 @@ struct MentionSuggestionItemView: View { } struct MentionSuggestionItemView_Previews: PreviewProvider, TestablePreview { - static let mockMediaProvider = MockMediaProvider() + static let mockMediaProvider = MediaProviderMock(configuration: .init()) static var previews: some View { MentionSuggestionItemView(mediaProvider: mockMediaProvider, item: .init(id: "test", displayName: "Test", avatarURL: URL.documentsDirectory, range: .init())) diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/RoomAttachmentPicker.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/RoomAttachmentPicker.swift index 0871f4f9db..791ee8d548 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/RoomAttachmentPicker.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/RoomAttachmentPicker.swift @@ -89,7 +89,7 @@ private struct RoomAttachmentPickerButtonStyle: ButtonStyle { struct RoomAttachmentPicker_Previews: PreviewProvider, TestablePreview { static let viewModel = ComposerToolbarViewModel(wysiwygViewModel: WysiwygComposerViewModel(), completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index b58e04c1a7..08beb380b2 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -196,7 +196,7 @@ extension RoomScreenViewModel { static func mock(roomProxyMock: JoinedRoomProxyMock) -> RoomScreenViewModel { RoomScreenViewModel(roomProxy: roomProxyMock, initialSelectedPinnedEventID: nil, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomHeaderView.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomHeaderView.swift index 68dc0a08c2..c7291c4412 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/RoomHeaderView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomHeaderView.swift @@ -42,7 +42,7 @@ struct RoomHeaderView_Previews: PreviewProvider, TestablePreview { roomAvatar: .room(id: "1", name: "Some Room Name", avatarURL: URL.picturesDirectory), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) .previewLayout(.sizeThatFits) .padding() @@ -50,7 +50,7 @@ struct RoomHeaderView_Previews: PreviewProvider, TestablePreview { roomAvatar: .room(id: "1", name: "Some Room Name", avatarURL: nil), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) .previewLayout(.sizeThatFits) .padding() } diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift index e1f1b81ea0..827e7bb229 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift @@ -214,7 +214,7 @@ struct RoomScreen_Previews: PreviewProvider, TestablePreview { static let roomViewModel = RoomScreenViewModel.mock(roomProxyMock: roomProxyMock) static let timelineViewModel = TimelineViewModel(roomProxy: roomProxyMock, timelineController: MockRoomTimelineController(), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, diff --git a/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/View/UserDetailsEditScreen.swift b/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/View/UserDetailsEditScreen.swift index ed6a29f866..9049acea55 100644 --- a/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/View/UserDetailsEditScreen.swift +++ b/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/View/UserDetailsEditScreen.swift @@ -116,7 +116,7 @@ struct UserDetailsEditScreen: View { struct UserDetailsEditScreen_Previews: PreviewProvider, TestablePreview { static let viewModel = UserDetailsEditScreenViewModel(clientProxy: ClientProxyMock(.init(userID: "@stefan:matrix.org")), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: UserIndicatorControllerMock.default) static var previews: some View { diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index 517cc7d568..0b01621447 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -845,7 +845,7 @@ extension TimelineViewModel { static let mock = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")), focussedEventID: nil, timelineController: MockRoomTimelineController(), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, @@ -856,7 +856,7 @@ extension TimelineViewModel { static let pinnedEventsTimelineMock = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")), focussedEventID: nil, timelineController: MockRoomTimelineController(timelineKind: .pinned), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, diff --git a/ElementX/Sources/Screens/Timeline/View/ReadReceipts/ReadReceiptCell.swift b/ElementX/Sources/Screens/Timeline/View/ReadReceipts/ReadReceiptCell.swift index 033e3c084e..e1328e605f 100644 --- a/ElementX/Sources/Screens/Timeline/View/ReadReceipts/ReadReceiptCell.swift +++ b/ElementX/Sources/Screens/Timeline/View/ReadReceipts/ReadReceiptCell.swift @@ -61,18 +61,18 @@ struct ReadReceiptCell_Previews: PreviewProvider, TestablePreview { formattedTimestamp: "10:00"), memberState: .init(displayName: "Test", avatarURL: nil), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) .previewDisplayName("No Image") ReadReceiptCell(readReceipt: .init(userID: "@test:matrix.org", formattedTimestamp: "10:00"), memberState: .init(displayName: "Test", avatarURL: URL.documentsDirectory), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) .previewDisplayName("With Image") ReadReceiptCell(readReceipt: .init(userID: "@test:matrix.org", formattedTimestamp: "10:00"), memberState: nil, - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) .previewDisplayName("Loading Member") } } diff --git a/ElementX/Sources/Screens/Timeline/View/ReadReceipts/ReadReceiptsSummaryView.swift b/ElementX/Sources/Screens/Timeline/View/ReadReceipts/ReadReceiptsSummaryView.swift index 50294df598..414266fa45 100644 --- a/ElementX/Sources/Screens/Timeline/View/ReadReceipts/ReadReceiptsSummaryView.swift +++ b/ElementX/Sources/Screens/Timeline/View/ReadReceipts/ReadReceiptsSummaryView.swift @@ -45,7 +45,7 @@ struct ReadReceiptsSummaryView_Previews: PreviewProvider, TestablePreview { let roomProxyMock = JoinedRoomProxyMock(.init(name: "Room", members: members)) let mock = TimelineViewModel(roomProxy: roomProxyMock, timelineController: MockRoomTimelineController(), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: UserIndicatorControllerMock(), diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift index 894d9ef529..9b0213f25c 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift @@ -359,7 +359,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview return TimelineViewModel(roomProxy: roomProxy, focussedEventID: nil, timelineController: MockRoomTimelineController(), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, diff --git a/ElementX/Sources/Screens/Timeline/View/Supplementary/ReactionsSummaryView.swift b/ElementX/Sources/Screens/Timeline/View/Supplementary/ReactionsSummaryView.swift index 130d612eb9..1920f2d64f 100644 --- a/ElementX/Sources/Screens/Timeline/View/Supplementary/ReactionsSummaryView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Supplementary/ReactionsSummaryView.swift @@ -139,7 +139,7 @@ struct ReactionsSummaryView_Previews: PreviewProvider, TestablePreview { static var previews: some View { ReactionsSummaryView(reactions: AggregatedReaction.mockReactions, members: [:], - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), selectedReactionKey: AggregatedReaction.mockReactions[0].key) } } diff --git a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift index 16d63650b6..ddf1c9b63e 100644 --- a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift @@ -83,7 +83,7 @@ struct TimelineReadReceiptsView_Previews: PreviewProvider, TestablePreview { static let viewModel = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Test", members: members)), timelineController: MockRoomTimelineController(), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/HighlightedTimelineItemModifier.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/HighlightedTimelineItemModifier.swift index 9b61793ff9..efb0566cd1 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/HighlightedTimelineItemModifier.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/HighlightedTimelineItemModifier.swift @@ -90,7 +90,7 @@ struct HighlightedTimelineItemTimeline_Previews: PreviewProvider { static let timelineViewModel = TimelineViewModel(roomProxy: roomProxyMock, focussedEventID: focussedEventID, timelineController: MockRoomTimelineController(), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineView.swift index c7dbde17d1..1090dbc56c 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineView.swift @@ -80,7 +80,7 @@ struct TimelineView_Previews: PreviewProvider, TestablePreview { static let roomViewModel = RoomScreenViewModel.mock(roomProxyMock: roomProxyMock) static let timelineViewModel = TimelineViewModel(roomProxy: roomProxyMock, timelineController: MockRoomTimelineController(), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, diff --git a/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift b/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift index a635e07319..b8d7f749a7 100644 --- a/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift +++ b/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift @@ -111,7 +111,7 @@ struct UserProfileScreen_Previews: PreviewProvider, TestablePreview { return UserProfileScreenViewModel(userID: userID, isPresentedModally: false, clientProxy: clientProxyMock, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController, analytics: ServiceLocator.shared.analytics) } diff --git a/ElementX/Sources/Services/Media/Provider/MediaProviderProtocol.swift b/ElementX/Sources/Services/Media/Provider/MediaProviderProtocol.swift index 7306b458e7..bae831b446 100644 --- a/ElementX/Sources/Services/Media/Provider/MediaProviderProtocol.swift +++ b/ElementX/Sources/Services/Media/Provider/MediaProviderProtocol.swift @@ -16,6 +16,7 @@ enum MediaProviderError: Error { case cancelled } +// sourcery: AutoMockable protocol MediaProviderProtocol { func imageFromSource(_ source: MediaSourceProxy?, size: CGSize?) -> UIImage? func loadImageFromSource(_ source: MediaSourceProxy, size: CGSize?) async -> Result diff --git a/ElementX/Sources/Services/Media/Provider/MockMediaProvider.swift b/ElementX/Sources/Services/Media/Provider/MockMediaProvider.swift deleted file mode 100644 index 3a77954da2..0000000000 --- a/ElementX/Sources/Services/Media/Provider/MockMediaProvider.swift +++ /dev/null @@ -1,62 +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 -import UIKit - -struct MockMediaProvider: MediaProviderProtocol { - func loadThumbnailForSource(source: MediaSourceProxy, size: CGSize) async -> Result { - fatalError("Not implemented") - } - - func imageFromSource(_ source: MediaSourceProxy?, size: CGSize?) -> UIImage? { - guard source != nil else { - return nil - } - - if source?.url == .picturesDirectory { - return Asset.Images.appLogo.image - } - - return UIImage(systemName: "photo") - } - - func loadImageFromSource(_ source: MediaSourceProxy, size: CGSize?) async -> Result { - guard let image = UIImage(systemName: "photo") else { - fatalError() - } - - return .success(image) - } - - func loadImageDataFromSource(_ source: MediaSourceProxy) async -> Result { - guard let image = UIImage(systemName: "photo"), - let data = image.pngData() else { - fatalError() - } - - return .success(data) - } - - var loadFileFromSourceReturnValue: MediaFileHandleProxy? - func loadFileFromSource(_ source: MediaSourceProxy, body: String?) async -> Result { - if let loadFileFromSourceReturnValue { - return .success(loadFileFromSourceReturnValue) - } - return .failure(.failedRetrievingFile) - } - - func loadImageRetryingOnReconnection(_ source: MediaSourceProxy, size: CGSize?) -> Task { - Task { - guard let image = UIImage(systemName: "photo") else { - fatalError() - } - - return image - } - } -} diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index 030e58bc37..493e333020 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -235,7 +235,7 @@ class MockScreen: Identifiable { let navigationStackCoordinator = NavigationStackCoordinator() let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Some room name", avatarURL: nil)), timelineController: MockRoomTimelineController(), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), @@ -253,7 +253,7 @@ class MockScreen: Identifiable { timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: URL.picturesDirectory)), timelineController: timelineController, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), @@ -271,7 +271,7 @@ class MockScreen: Identifiable { timelineController.timelineItems = RoomTimelineItemFixtures.default let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: URL.picturesDirectory)), timelineController: timelineController, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), @@ -289,7 +289,7 @@ class MockScreen: Identifiable { timelineController.timelineItems = RoomTimelineItemFixtures.smallChunkWithReadReceipts let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: URL.picturesDirectory)), timelineController: timelineController, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), @@ -310,7 +310,7 @@ class MockScreen: Identifiable { timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage] let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Small timeline", avatarURL: URL.picturesDirectory)), timelineController: timelineController, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), @@ -331,7 +331,7 @@ class MockScreen: Identifiable { timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk] let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Small timeline, paginating", avatarURL: URL.picturesDirectory)), timelineController: timelineController, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), @@ -352,7 +352,7 @@ class MockScreen: Identifiable { timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk] let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: URL.picturesDirectory)), timelineController: timelineController, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), @@ -374,7 +374,7 @@ class MockScreen: Identifiable { timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage] let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: URL.picturesDirectory)), timelineController: timelineController, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), @@ -395,7 +395,7 @@ class MockScreen: Identifiable { timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage] let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: URL.picturesDirectory)), timelineController: timelineController, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), @@ -415,7 +415,7 @@ class MockScreen: Identifiable { timelineController.timelineItems = RoomTimelineItemFixtures.permalinkChunk let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Timeline highlight", avatarURL: URL.picturesDirectory)), timelineController: timelineController, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), @@ -449,7 +449,7 @@ class MockScreen: Identifiable { timelineController.incomingItems = [] let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: URL.picturesDirectory)), timelineController: timelineController, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), @@ -470,7 +470,7 @@ class MockScreen: Identifiable { timelineController.incomingItems = [] let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: URL.picturesDirectory)), timelineController: timelineController, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), @@ -491,7 +491,7 @@ class MockScreen: Identifiable { timelineController.incomingItems = [] let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: URL.picturesDirectory)), timelineController: timelineController, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), @@ -542,7 +542,7 @@ class MockScreen: Identifiable { case .roomMembersListScreenPendingInvites: let navigationStackCoordinator = NavigationStackCoordinator() let members: [RoomMemberProxyMock] = [.mockInvitedAlice, .mockBob, .mockCharlie] - let coordinator = RoomMembersListScreenCoordinator(parameters: .init(mediaProvider: MockMediaProvider(), + let coordinator = RoomMembersListScreenCoordinator(parameters: .init(mediaProvider: MediaProviderMock(configuration: .init()), roomProxy: JoinedRoomProxyMock(.init(name: "test", members: members)), userIndicatorController: ServiceLocator.shared.userIndicatorController, analytics: ServiceLocator.shared.analytics)) @@ -552,7 +552,7 @@ class MockScreen: Identifiable { let navigationStackCoordinator = NavigationStackCoordinator() navigationStackCoordinator.setRootCoordinator(BlankFormCoordinator()) let coordinator = RoomRolesAndPermissionsFlowCoordinator(parameters: .init(roomProxy: JoinedRoomProxyMock(.init(members: .allMembersAsAdmin)), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), navigationStackCoordinator: navigationStackCoordinator, userIndicatorController: ServiceLocator.shared.userIndicatorController, analytics: ServiceLocator.shared.analytics)) diff --git a/UnitTests/Sources/BlockedUsersScreenViewModelTests.swift b/UnitTests/Sources/BlockedUsersScreenViewModelTests.swift index c79e725f32..0852dede1e 100644 --- a/UnitTests/Sources/BlockedUsersScreenViewModelTests.swift +++ b/UnitTests/Sources/BlockedUsersScreenViewModelTests.swift @@ -17,7 +17,7 @@ class BlockedUsersScreenViewModelTests: XCTestCase { let viewModel = BlockedUsersScreenViewModel(hideProfiles: true, clientProxy: clientProxy, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController) let deferred = deferFailure(viewModel.context.$viewState, timeout: 1) { $0.blockedUsers.contains(where: { $0.displayName != nil }) } @@ -32,7 +32,7 @@ class BlockedUsersScreenViewModelTests: XCTestCase { let viewModel = BlockedUsersScreenViewModel(hideProfiles: false, clientProxy: clientProxy, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController) let deferred = deferFulfillment(viewModel.context.$viewState) { $0.blockedUsers.contains(where: { $0.displayName != nil }) } diff --git a/UnitTests/Sources/ComposerToolbarViewModelTests.swift b/UnitTests/Sources/ComposerToolbarViewModelTests.swift index d4bf5fae0a..f869343d32 100644 --- a/UnitTests/Sources/ComposerToolbarViewModelTests.swift +++ b/UnitTests/Sources/ComposerToolbarViewModelTests.swift @@ -28,7 +28,7 @@ class ComposerToolbarViewModelTests: XCTestCase { draftServiceMock = ComposerDraftServiceMock() viewModel = ComposerToolbarViewModel(wysiwygViewModel: wysiwygViewModel, completionSuggestionService: completionSuggestionServiceMock, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, analyticsService: ServiceLocator.shared.analytics, composerDraftService: draftServiceMock) @@ -105,7 +105,7 @@ class ComposerToolbarViewModelTests: XCTestCase { let mockCompletionSuggestionService = CompletionSuggestionServiceMock(configuration: .init(suggestions: suggestions)) viewModel = ComposerToolbarViewModel(wysiwygViewModel: wysiwygViewModel, completionSuggestionService: mockCompletionSuggestionService, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mentionDisplayHelper: ComposerMentionDisplayHelper.mock, analyticsService: ServiceLocator.shared.analytics, composerDraftService: draftServiceMock) diff --git a/UnitTests/Sources/GlobalSearchScreenViewModelTests.swift b/UnitTests/Sources/GlobalSearchScreenViewModelTests.swift index 40c72f959d..7c6d4bcd28 100644 --- a/UnitTests/Sources/GlobalSearchScreenViewModelTests.swift +++ b/UnitTests/Sources/GlobalSearchScreenViewModelTests.swift @@ -19,7 +19,7 @@ class GlobalSearchScreenViewModelTests: XCTestCase { override func setUpWithError() throws { cancellables.removeAll() viewModel = GlobalSearchScreenViewModel(roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) context = viewModel.context } diff --git a/UnitTests/Sources/InviteUsersViewModelTests.swift b/UnitTests/Sources/InviteUsersViewModelTests.swift index 5e3c463681..fb79524cac 100644 --- a/UnitTests/Sources/InviteUsersViewModelTests.swift +++ b/UnitTests/Sources/InviteUsersViewModelTests.swift @@ -92,7 +92,7 @@ class InviteUsersScreenViewModelTests: XCTestCase { let viewModel = InviteUsersScreenViewModel(clientProxy: ClientProxyMock(.init()), selectedUsers: usersSubject.asCurrentValuePublisher(), roomType: roomType, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userDiscoveryService: userDiscoveryService, userIndicatorController: UserIndicatorControllerMock()) viewModel.state.usersSection = .init(type: .suggestions, users: [.mockAlice, .mockBob, .mockCharlie]) diff --git a/UnitTests/Sources/JoinRoomScreenViewModelTests.swift b/UnitTests/Sources/JoinRoomScreenViewModelTests.swift index ea2ef31a48..c0a0984cee 100644 --- a/UnitTests/Sources/JoinRoomScreenViewModelTests.swift +++ b/UnitTests/Sources/JoinRoomScreenViewModelTests.swift @@ -63,7 +63,7 @@ class JoinRoomScreenViewModelTests: XCTestCase { viewModel = JoinRoomScreenViewModel(roomID: "1", via: [], clientProxy: clientProxy, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController) } } diff --git a/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift b/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift index e1705b3882..b0c8e2c96d 100644 --- a/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift +++ b/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift @@ -29,7 +29,7 @@ class MessageForwardingScreenViewModelTests: XCTestCase { clientProxy: clientProxy, roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))), userIndicatorController: UserIndicatorControllerMock(), - mediaProvider: MockMediaProvider()) + mediaProvider: MediaProviderMock(configuration: .init())) context = viewModel.context } diff --git a/UnitTests/Sources/PillContextTests.swift b/UnitTests/Sources/PillContextTests.swift index 12b926b481..03b1b462e0 100644 --- a/UnitTests/Sources/PillContextTests.swift +++ b/UnitTests/Sources/PillContextTests.swift @@ -19,7 +19,7 @@ class PillContextTests: XCTestCase { proxyMock.membersPublisher = subject.asCurrentValuePublisher() let mock = TimelineViewModel(roomProxy: proxyMock, timelineController: MockRoomTimelineController(), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, @@ -47,7 +47,7 @@ class PillContextTests: XCTestCase { proxyMock.membersPublisher = subject.asCurrentValuePublisher() let mock = TimelineViewModel(roomProxy: proxyMock, timelineController: MockRoomTimelineController(), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, @@ -68,7 +68,7 @@ class PillContextTests: XCTestCase { mockController.roomProxy = proxyMock let mock = TimelineViewModel(roomProxy: proxyMock, timelineController: mockController, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, diff --git a/UnitTests/Sources/RoomChangeRolesScreenViewModelTests.swift b/UnitTests/Sources/RoomChangeRolesScreenViewModelTests.swift index 427ff39e90..b78bb60c88 100644 --- a/UnitTests/Sources/RoomChangeRolesScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomChangeRolesScreenViewModelTests.swift @@ -196,7 +196,7 @@ class RoomChangeRolesScreenViewModelTests: XCTestCase { roomProxy = JoinedRoomProxyMock(.init(members: .allMembersAsAdmin)) viewModel = RoomChangeRolesScreenViewModel(mode: mode, roomProxy: roomProxy, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: UserIndicatorControllerMock(), analytics: ServiceLocator.shared.analytics) } diff --git a/UnitTests/Sources/RoomDetailsEditScreenViewModelTests.swift b/UnitTests/Sources/RoomDetailsEditScreenViewModelTests.swift index ad79d87fa2..a21ec8c104 100644 --- a/UnitTests/Sources/RoomDetailsEditScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomDetailsEditScreenViewModelTests.swift @@ -111,7 +111,7 @@ class RoomDetailsEditScreenViewModelTests: XCTestCase { private func setupViewModel(roomProxyConfiguration: JoinedRoomProxyMockConfiguration) { userIndicatorController = UserIndicatorControllerMock.default viewModel = .init(roomProxy: JoinedRoomProxyMock(roomProxyConfiguration), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: userIndicatorController) } } diff --git a/UnitTests/Sources/RoomDetailsViewModelTests.swift b/UnitTests/Sources/RoomDetailsViewModelTests.swift index bd8e09abe1..0908be9190 100644 --- a/UnitTests/Sources/RoomDetailsViewModelTests.swift +++ b/UnitTests/Sources/RoomDetailsViewModelTests.swift @@ -26,7 +26,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { notificationSettingsProxyMock = NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()) viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: notificationSettingsProxyMock, @@ -42,7 +42,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isPublic: true, members: mockedMembers)) viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), @@ -65,7 +65,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isPublic: false, members: mockedMembers)) viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), @@ -89,7 +89,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isPublic: false, members: mockedMembers)) viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), @@ -143,7 +143,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isDirect: true, isEncrypted: true, members: mockedMembers)) viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), @@ -167,7 +167,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isDirect: true, isEncrypted: true, members: mockedMembers)) viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), @@ -202,7 +202,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isDirect: true, isEncrypted: true, members: mockedMembers)) viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: clientProxy, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), @@ -236,7 +236,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isDirect: true, isEncrypted: true, members: mockedMembers)) viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), @@ -271,7 +271,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isDirect: true, isEncrypted: true, members: mockedMembers)) viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: clientProxy, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), @@ -307,7 +307,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { canUserInvite: false)) viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), @@ -325,7 +325,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isPublic: true, members: mockedMembers)) viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), @@ -362,7 +362,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { } viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), @@ -386,7 +386,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { } viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), @@ -410,7 +410,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { } viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), @@ -431,7 +431,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isDirect: false, isPublic: false, members: mockedMembers)) viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), @@ -452,7 +452,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isDirect: true, isPublic: false, members: mockedMembers)) viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), @@ -471,7 +471,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { notificationSettingsProxyMock.getNotificationSettingsRoomIdIsEncryptedIsOneToOneThrowableError = NotificationSettingsError.Generic(msg: "error") viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), analyticsService: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, notificationSettingsProxy: notificationSettingsProxyMock, diff --git a/UnitTests/Sources/RoomMemberDetailsViewModelTests.swift b/UnitTests/Sources/RoomMemberDetailsViewModelTests.swift index aa79f28b8a..25c8f61014 100644 --- a/UnitTests/Sources/RoomMemberDetailsViewModelTests.swift +++ b/UnitTests/Sources/RoomMemberDetailsViewModelTests.swift @@ -29,7 +29,7 @@ class RoomMemberDetailsViewModelTests: XCTestCase { viewModel = RoomMemberDetailsScreenViewModel(userID: roomMemberProxyMock.userID, roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController, analytics: ServiceLocator.shared.analytics) @@ -46,7 +46,7 @@ class RoomMemberDetailsViewModelTests: XCTestCase { viewModel = RoomMemberDetailsScreenViewModel(userID: roomMemberProxyMock.userID, roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController, analytics: ServiceLocator.shared.analytics) @@ -84,7 +84,7 @@ class RoomMemberDetailsViewModelTests: XCTestCase { viewModel = RoomMemberDetailsScreenViewModel(userID: roomMemberProxyMock.userID, roomProxy: roomProxyMock, clientProxy: clientProxy, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController, analytics: ServiceLocator.shared.analytics) @@ -121,7 +121,7 @@ class RoomMemberDetailsViewModelTests: XCTestCase { viewModel = RoomMemberDetailsScreenViewModel(userID: roomMemberProxyMock.userID, roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController, analytics: ServiceLocator.shared.analytics) @@ -157,7 +157,7 @@ class RoomMemberDetailsViewModelTests: XCTestCase { viewModel = RoomMemberDetailsScreenViewModel(userID: roomMemberProxyMock.userID, roomProxy: roomProxyMock, clientProxy: clientProxy, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController, analytics: ServiceLocator.shared.analytics) @@ -193,7 +193,7 @@ class RoomMemberDetailsViewModelTests: XCTestCase { viewModel = RoomMemberDetailsScreenViewModel(userID: roomMemberProxyMock.userID, roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController, analytics: ServiceLocator.shared.analytics) @@ -210,7 +210,7 @@ class RoomMemberDetailsViewModelTests: XCTestCase { viewModel = RoomMemberDetailsScreenViewModel(userID: roomMemberProxyMock.userID, roomProxy: roomProxyMock, clientProxy: ClientProxyMock(.init()), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController, analytics: ServiceLocator.shared.analytics) diff --git a/UnitTests/Sources/RoomMembersListScreenViewModelTests.swift b/UnitTests/Sources/RoomMembersListScreenViewModelTests.swift index ba4ccfb24e..8dd9f0cc9e 100644 --- a/UnitTests/Sources/RoomMembersListScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomMembersListScreenViewModelTests.swift @@ -279,7 +279,7 @@ class RoomMembersListScreenViewModelTests: XCTestCase { private func setup(with members: [RoomMemberProxyMock]) { roomProxy = JoinedRoomProxyMock(.init(name: "test", members: members)) viewModel = .init(roomProxy: roomProxy, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController, analytics: ServiceLocator.shared.analytics) } diff --git a/UnitTests/Sources/RoomScreenViewModelTests.swift b/UnitTests/Sources/RoomScreenViewModelTests.swift index 20207bf013..da9196d2cb 100644 --- a/UnitTests/Sources/RoomScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomScreenViewModelTests.swift @@ -35,7 +35,7 @@ class RoomScreenViewModelTests: XCTestCase { roomProxyMock.underlyingActionsPublisher = updateSubject.eraseToAnyPublisher() let viewModel = RoomScreenViewModel(roomProxy: roomProxyMock, initialSelectedPinnedEventID: nil, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, @@ -113,7 +113,7 @@ class RoomScreenViewModelTests: XCTestCase { roomProxyMock.underlyingPinnedEventsTimeline = pinnedTimelineMock let viewModel = RoomScreenViewModel(roomProxy: roomProxyMock, initialSelectedPinnedEventID: "test1", - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, @@ -160,7 +160,7 @@ class RoomScreenViewModelTests: XCTestCase { roomProxyMock.underlyingActionsPublisher = updateSubject.eraseToAnyPublisher() let viewModel = RoomScreenViewModel(roomProxy: roomProxyMock, initialSelectedPinnedEventID: nil, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, @@ -197,7 +197,7 @@ class RoomScreenViewModelTests: XCTestCase { let roomProxyMock = JoinedRoomProxyMock(.init(id: "MyRoomID")) let viewModel = RoomScreenViewModel(roomProxy: roomProxyMock, initialSelectedPinnedEventID: nil, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), ongoingCallRoomIDPublisher: ongoingCallRoomIDSubject.asCurrentValuePublisher(), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, diff --git a/UnitTests/Sources/TimelineViewModelTests.swift b/UnitTests/Sources/TimelineViewModelTests.swift index 062aff86c7..d1dcfc69b3 100644 --- a/UnitTests/Sources/TimelineViewModelTests.swift +++ b/UnitTests/Sources/TimelineViewModelTests.swift @@ -303,7 +303,7 @@ class TimelineViewModelTests: XCTestCase { let viewModel = TimelineViewModel(roomProxy: roomProxy, timelineController: timelineController, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, @@ -327,7 +327,7 @@ class TimelineViewModelTests: XCTestCase { timelineController.timelineItems = [message] let viewModel = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "", members: [RoomMemberProxyMock.mockAlice, RoomMemberProxyMock.mockCharlie])), timelineController: timelineController, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, @@ -353,7 +353,7 @@ class TimelineViewModelTests: XCTestCase { let viewModel = TimelineViewModel(roomProxy: roomProxyMock, timelineController: MockRoomTimelineController(), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, @@ -381,7 +381,7 @@ class TimelineViewModelTests: XCTestCase { let viewModel = TimelineViewModel(roomProxy: roomProxyMock, timelineController: MockRoomTimelineController(), - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, @@ -410,7 +410,7 @@ class TimelineViewModelTests: XCTestCase { TimelineViewModel(roomProxy: roomProxy ?? JoinedRoomProxyMock(.init(name: "")), focussedEventID: focussedEventID, timelineController: timelineController, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, diff --git a/UnitTests/Sources/UserProfileScreenViewModelTests.swift b/UnitTests/Sources/UserProfileScreenViewModelTests.swift index ecd4d2efda..f040de8c28 100644 --- a/UnitTests/Sources/UserProfileScreenViewModelTests.swift +++ b/UnitTests/Sources/UserProfileScreenViewModelTests.swift @@ -22,7 +22,7 @@ class UserProfileScreenViewModelTests: XCTestCase { viewModel = UserProfileScreenViewModel(userID: profile.userID, isPresentedModally: false, clientProxy: clientProxy, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController, analytics: ServiceLocator.shared.analytics) @@ -42,7 +42,7 @@ class UserProfileScreenViewModelTests: XCTestCase { viewModel = UserProfileScreenViewModel(userID: profile.userID, isPresentedModally: false, clientProxy: clientProxy, - mediaProvider: MockMediaProvider(), + mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController, analytics: ServiceLocator.shared.analytics) diff --git a/UnitTests/Sources/VoiceMessageMediaManagerTests.swift b/UnitTests/Sources/VoiceMessageMediaManagerTests.swift index 644bdd62ea..8d8ed12812 100644 --- a/UnitTests/Sources/VoiceMessageMediaManagerTests.swift +++ b/UnitTests/Sources/VoiceMessageMediaManagerTests.swift @@ -14,14 +14,14 @@ import XCTest class VoiceMessageMediaManagerTests: XCTestCase { private var voiceMessageMediaManager: VoiceMessageMediaManager! private var voiceMessageCache: VoiceMessageCacheMock! - private var mediaProvider: MockMediaProvider! + private var mediaProvider: MediaProviderMock! private let someURL = URL("/some/url") private let audioOGGMimeType = "audio/ogg" override func setUp() async throws { voiceMessageCache = VoiceMessageCacheMock() - mediaProvider = MockMediaProvider() + mediaProvider = MediaProviderMock(configuration: .init()) voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, voiceMessageCache: voiceMessageCache) } @@ -50,7 +50,7 @@ class VoiceMessageMediaManagerTests: XCTestCase { voiceMessageCache.fileURLForReturnValue = nil let mediaSource = MediaSourceProxy(url: someURL, mimeType: "audio/ogg; codecs=opus") - mediaProvider.loadFileFromSourceReturnValue = MediaFileHandleProxy.unmanaged(url: loadedFile) + mediaProvider.loadFileFromSourceBodyReturnValue = .success(MediaFileHandleProxy.unmanaged(url: loadedFile)) voiceMessageCache.cacheMediaSourceUsingMoveReturnValue = .success(cachedConvertedFileURL) voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, @@ -103,7 +103,7 @@ class VoiceMessageMediaManagerTests: XCTestCase { // Check if the file is not already present in cache voiceMessageCache.fileURLForReturnValue = nil let mediaSource = MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType) - mediaProvider.loadFileFromSourceReturnValue = MediaFileHandleProxy.unmanaged(url: loadedFile) + mediaProvider.loadFileFromSourceBodyReturnValue = .success(MediaFileHandleProxy.unmanaged(url: loadedFile)) let audioConverter = AudioConverterMock() voiceMessageCache.cacheMediaSourceUsingMoveReturnValue = .success(cachedConvertedFileURL) voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, @@ -139,7 +139,7 @@ class VoiceMessageMediaManagerTests: XCTestCase { } let audioConverter = AudioConverterMock() - mediaProvider.loadFileFromSourceReturnValue = MediaFileHandleProxy.unmanaged(url: loadedFile) + mediaProvider.loadFileFromSourceBodyReturnValue = .success(MediaFileHandleProxy.unmanaged(url: loadedFile)) voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, voiceMessageCache: voiceMessageCache, From 2a2a823861db28ecf05ad5ec97ae7a536562b88a Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Thu, 3 Oct 2024 13:29:20 +0300 Subject: [PATCH 028/114] Remove spammy typing notification sending log --- ElementX/Sources/Services/Room/JoinedRoomProxy.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/ElementX/Sources/Services/Room/JoinedRoomProxy.swift b/ElementX/Sources/Services/Room/JoinedRoomProxy.swift index e23cd573a1..b1fcd8bf7c 100644 --- a/ElementX/Sources/Services/Room/JoinedRoomProxy.swift +++ b/ElementX/Sources/Services/Room/JoinedRoomProxy.swift @@ -369,8 +369,6 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol { } func sendTypingNotification(isTyping: Bool) async -> Result { - MXLog.info("Sending typing notification isTyping: \(isTyping)") - do { try await room.typingNotice(isTyping: isTyping) return .success(()) From 4f29821306208c023a38897509c7c28b941b8f4f Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Thu, 3 Oct 2024 13:31:36 +0300 Subject: [PATCH 029/114] Workaround for #1786 - Disable auto correction when running on the Mac --- .../ComposerToolbar/View/MessageComposerTextField.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposerTextField.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposerTextField.swift index 05469baf14..a1f73a0011 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposerTextField.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposerTextField.swift @@ -79,6 +79,12 @@ private struct UITextViewWrapper: UIViewRepresentable { textView.textContainer.lineFragmentPadding = 0.0 textView.textContainerInset = .zero textView.keyboardType = .default + + // AutoCorrection doesn't work properly when running on the Mac + // https://github.com/element-hq/element-x-ios/issues/1786 + if ProcessInfo.processInfo.isiOSAppOnMac { + textView.autocorrectionType = .no + } textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) From e6f4dd33a0326081a03553f3d8f6a21ca3c2041b Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Thu, 3 Oct 2024 17:59:39 +0100 Subject: [PATCH 030/114] Add developer option to hide media in the timeline. (#3366) --- .../en.lproj/Localizable.strings | 5 +- .../Sources/Application/AppSettings.swift | 5 + ElementX/Sources/Generated/Strings.swift | 9 +- .../SwiftUI/Views/LoadableAvatarImage.swift | 1 + .../Other/SwiftUI/Views/LoadableImage.swift | 227 ++++++++++++++++-- .../View/RoomPollsHistoryScreen.swift | 2 +- .../Screens/RoomScreen/View/RoomScreen.swift | 2 + .../DeveloperOptionsScreenModels.swift | 3 +- .../View/DeveloperOptionsScreen.swift | 6 + .../Screens/Timeline/TimelineModels.swift | 1 + .../TimelineTableViewController.swift | 13 +- .../Screens/Timeline/TimelineViewModel.swift | 5 + .../ImageRoomTimelineView.swift | 12 +- .../StickerRoomTimelineView.swift | 12 +- .../VideoRoomTimelineView.swift | 12 +- .../Screens/Timeline/View/TimelineView.swift | 3 + .../Fixtures/RoomTimelineItemFixtures.swift | 38 ++- NSE/Sources/NotificationContentBuilder.swift | 3 + .../NotificationServiceExtension.swift | 3 +- .../Sources/GeneratedPreviewTests.swift | 6 + .../test_loadableImage-iPad-en-GB.1.png | 3 + .../test_loadableImage-iPad-pseudo.1.png | 3 + .../test_loadableImage-iPhone-16-en-GB.1.png | 3 + .../test_loadableImage-iPhone-16-pseudo.1.png | 3 + 24 files changed, 320 insertions(+), 60 deletions(-) create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_loadableImage-iPad-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_loadableImage-iPad-pseudo.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_loadableImage-iPhone-16-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_loadableImage-iPhone-16-pseudo.1.png diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index f592805304..413594832b 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -41,6 +41,7 @@ "action_create" = "Create"; "action_create_a_room" = "Create a room"; "action_deactivate" = "Deactivate"; +"action_deactivate_account" = "Deactivate account"; "action_decline" = "Decline"; "action_delete_poll" = "Delete Poll"; "action_disable" = "Disable"; @@ -64,6 +65,7 @@ "action_leave" = "Leave"; "action_leave_conversation" = "Leave conversation"; "action_leave_room" = "Leave room"; +"action_load_more" = "Load more"; "action_manage_account" = "Manage account"; "action_manage_devices" = "Manage devices"; "action_message" = "Message"; @@ -93,6 +95,7 @@ "action_send_message" = "Send message"; "action_share" = "Share"; "action_share_link" = "Share link"; +"action_show" = "Show"; "action_sign_in_again" = "Sign in again"; "action_signout" = "Sign out"; "action_signout_anyway" = "Sign out anyway"; @@ -108,8 +111,6 @@ "action_view_in_timeline" = "View in timeline"; "action_view_source" = "View source"; "action_yes" = "Yes"; -"action.load_more" = "Load more"; -"action_deactivate_account" = "Deactivate account"; "banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index 3f8db72047..542d58057a 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -12,6 +12,7 @@ import SwiftUI protocol CommonSettingsProtocol { var logLevel: TracingConfiguration.LogLevel { get } var enableOnlySignedDeviceIsolationMode: Bool { get } + var hideTimelineMedia: Bool { get } } /// Store Element specific app settings. @@ -34,6 +35,7 @@ final class AppSettings { case appAppearance case sharePresence case hideUnreadMessagesBadge + case hideTimelineMedia case elementCallBaseURLOverride case elementCallEncryptionEnabled @@ -285,6 +287,9 @@ final class AppSettings { /// Configuration to enable only signed device isolation mode for crypto. In this mode only devices signed by their owner will be considered in e2ee rooms. @UserPreference(key: UserDefaultsKeys.enableOnlySignedDeviceIsolationMode, defaultValue: false, storageType: .userDefaults(store)) var enableOnlySignedDeviceIsolationMode + + @UserPreference(key: UserDefaultsKeys.hideTimelineMedia, defaultValue: false, storageType: .userDefaults(store)) + var hideTimelineMedia } extension AppSettings: CommonSettingsProtocol { } diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 29098b6e85..55bb4b85db 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -164,6 +164,8 @@ internal enum L10n { internal static var actionLeaveConversation: String { return L10n.tr("Localizable", "action_leave_conversation") } /// Leave room internal static var actionLeaveRoom: String { return L10n.tr("Localizable", "action_leave_room") } + /// Load more + internal static var actionLoadMore: String { return L10n.tr("Localizable", "action_load_more") } /// Manage account internal static var actionManageAccount: String { return L10n.tr("Localizable", "action_manage_account") } /// Manage devices @@ -222,6 +224,8 @@ internal enum L10n { internal static var actionShare: String { return L10n.tr("Localizable", "action_share") } /// Share link internal static var actionShareLink: String { return L10n.tr("Localizable", "action_share_link") } + /// Show + internal static var actionShow: String { return L10n.tr("Localizable", "action_show") } /// Sign in again internal static var actionSignInAgain: String { return L10n.tr("Localizable", "action_sign_in_again") } /// Sign out @@ -2396,11 +2400,6 @@ internal enum L10n { /// Check UnifiedPush internal static var troubleshootNotificationsTestUnifiedPushTitle: String { return L10n.tr("Localizable", "troubleshoot_notifications_test_unified_push_title") } - internal enum Action { - /// Load more - internal static var loadMore: String { return L10n.tr("Localizable", "action.load_more") } - } - internal enum Banner { internal enum SetUpRecovery { /// Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices. diff --git a/ElementX/Sources/Other/SwiftUI/Views/LoadableAvatarImage.swift b/ElementX/Sources/Other/SwiftUI/Views/LoadableAvatarImage.swift index 2c35c9a5e9..7daf4655de 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/LoadableAvatarImage.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/LoadableAvatarImage.swift @@ -50,6 +50,7 @@ struct LoadableAvatarImage: View { .frame(width: frameSize, height: frameSize) .background(Color.compound.bgCanvasDefault) .clipShape(Circle()) + .environment(\.shouldAutomaticallyLoadImages, true) // We always load avatars. } @ViewBuilder diff --git a/ElementX/Sources/Other/SwiftUI/Views/LoadableImage.swift b/ElementX/Sources/Other/SwiftUI/Views/LoadableImage.swift index d1183ca400..6bf098b997 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/LoadableImage.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/LoadableImage.swift @@ -6,12 +6,17 @@ // import Combine +import Compound import Kingfisher import SwiftUI /// Used to configure animations enum LoadableImageMediaType { + /// An avatar (can be displayed anywhere within the app). case avatar + /// An image displayed in the timeline. + case timelineItem + /// Any other media (can be displayed anywhere within the app). case generic } @@ -79,6 +84,8 @@ struct LoadableImage: View { } private struct LoadableImageContent: View, ImageDataProvider { + @Environment(\.shouldAutomaticallyLoadImages) private var loadAutomatically + private let mediaSource: MediaSourceProxy private let mediaType: LoadableImageMediaType private let blurhash: String? @@ -86,6 +93,7 @@ private struct LoadableImageContent PlaceholderView @StateObject private var contentLoader: ContentLoader + @State private var loadManually = false init(mediaSource: MediaSourceProxy, mediaType: LoadableImageMediaType, @@ -104,36 +112,40 @@ private struct LoadableImageContent some View { Color.compound._bgBubbleIncoming } + static func transformer(_ view: AnyView) -> some View { + view.overlay { + Image(systemSymbol: .playCircleFill) + .font(.largeTitle) + .foregroundStyle(.compound.iconAccentPrimary) + } + } + + static func makeMediaProvider(isLoading: Bool = false) -> MediaProviderProtocol { + let mediaProvider = MediaProviderMock(configuration: .init()) + + if isLoading { + mediaProvider.imageFromSourceSizeClosure = { _, _ in nil } + mediaProvider.loadFileFromSourceBodyClosure = { _, _ in .failure(.failedRetrievingFile) } + mediaProvider.loadImageDataFromSourceClosure = { _ in .failure(.failedRetrievingImage) } + mediaProvider.loadImageFromSourceSizeClosure = { _, _ in .failure(.failedRetrievingImage) } + mediaProvider.loadThumbnailForSourceSourceSizeClosure = { _, _ in .failure(.failedRetrievingThumbnail) } + mediaProvider.loadImageRetryingOnReconnectionSizeClosure = { _, _ in + Task { throw MediaProviderError.failedRetrievingImage } + } + } + return mediaProvider + } +} + +private extension View { + func layout(title: String, hideTimelineMedia: Bool = false) -> some View { + aspectRatio(contentMode: .fit) + .clipShape(RoundedRectangle(cornerRadius: 20)) + .overlay(alignment: .bottom) { + Text(title) + .font(.caption2) + .offset(y: 16) + .padding(.horizontal, -5) + } + .environment(\.shouldAutomaticallyLoadImages, !hideTimelineMedia) + } +} diff --git a/ElementX/Sources/Screens/RoomPollsHistoryScreen/View/RoomPollsHistoryScreen.swift b/ElementX/Sources/Screens/RoomPollsHistoryScreen/View/RoomPollsHistoryScreen.swift index 9358e02270..96d4c8c548 100644 --- a/ElementX/Sources/Screens/RoomPollsHistoryScreen/View/RoomPollsHistoryScreen.swift +++ b/ElementX/Sources/Screens/RoomPollsHistoryScreen/View/RoomPollsHistoryScreen.swift @@ -91,7 +91,7 @@ struct RoomPollsHistoryScreen: View { Button { context.send(viewAction: .loadMore) } label: { - Text(L10n.Action.loadMore) + Text(L10n.actionLoadMore) .font(.compound.bodyLGSemibold) .padding(.horizontal, 12) } diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift index 827e7bb229..dee0516fb5 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift @@ -42,6 +42,8 @@ struct RoomScreen: View { .background(Color.compound.bgCanvasDefault.ignoresSafeArea()) .environmentObject(timelineContext) .environment(\.timelineContext, timelineContext) + // Make sure the reply header honours the hideTimelineMedia setting too. + .environment(\.shouldAutomaticallyLoadImages, !timelineContext.viewState.hideTimelineMedia) } .overlay(alignment: .top) { Group { diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift index 15b1f7d682..d2f9f7a4b4 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift @@ -44,9 +44,10 @@ protocol DeveloperOptionsProtocol: AnyObject { var logLevel: TracingConfiguration.LogLevel { get set } var slidingSyncDiscovery: AppSettings.SlidingSyncDiscovery { get set } var hideUnreadMessagesBadge: Bool { get set } - var elementCallBaseURLOverride: URL? { get set } var fuzzyRoomListSearchEnabled: Bool { get set } + var hideTimelineMedia: Bool { get set } var enableOnlySignedDeviceIsolationMode: Bool { get set } + var elementCallBaseURLOverride: URL? { get set } } extension AppSettings: DeveloperOptionsProtocol { } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift index 723ae359d5..683b0a1f1b 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -45,6 +45,12 @@ struct DeveloperOptionsScreen: View { } } + Section("Timeline") { + Toggle(isOn: $context.hideTimelineMedia) { + Text("Hide image & video previews") + } + } + Section { Toggle(isOn: $context.enableOnlySignedDeviceIsolationMode) { Text("Exclude not secure devices when sending/receiving messages") diff --git a/ElementX/Sources/Screens/Timeline/TimelineModels.swift b/ElementX/Sources/Screens/Timeline/TimelineModels.swift index ab72290152..6bfcc9b9ca 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineModels.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineModels.swift @@ -97,6 +97,7 @@ struct TimelineViewState: BindableState { var canCurrentUserRedactSelf = false var canCurrentUserPin = false var isViewSourceEnabled: Bool + var hideTimelineMedia: Bool // The `pinnedEventIDs` are used only to determine if an item is already pinned or not. // It's updated from the room info, so it's faster than using the timeline diff --git a/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift b/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift index 6b32e9c74c..8876749e8f 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift @@ -125,6 +125,13 @@ class TimelineTableViewController: UIViewController { } } + var hideTimelineMedia = false { + didSet { + guard let snapshot = dataSource?.snapshot() else { return } + dataSource?.applySnapshotUsingReloadData(snapshot) + } + } + /// Used to hold an observable object that the typing indicator can use let typingMembers = TypingMembersObservableObject(members: []) @@ -260,21 +267,19 @@ class TimelineTableViewController: UIViewController { let cell = tableView.dequeueReusableCell(withIdentifier: TimelineItemCell.reuseIdentifier, for: indexPath) guard let self, let cell = cell as? TimelineItemCell else { return cell } - // A local reference to avoid capturing self in the cell configuration. - let coordinator = self.coordinator - let viewState = timelineItemsDictionary[id] cell.item = viewState guard let viewState else { return cell } - cell.contentConfiguration = UIHostingConfiguration { + cell.contentConfiguration = UIHostingConfiguration { [coordinator, hideTimelineMedia] in RoomTimelineItemView(viewState: viewState) .id(id) .frame(maxWidth: .infinity, alignment: .leading) .environmentObject(coordinator.context) // Attempted fix at a crash in TimelineItemContextMenu .environment(\.timelineContext, coordinator.context) + .environment(\.shouldAutomaticallyLoadImages, !hideTimelineMedia) } .margins(.all, 0) // Margins are handled in the stylers .minSize(height: 1) diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index 0b01621447..2dc59055fa 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -78,6 +78,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { timelineViewState: TimelineState(focussedEvent: focussedEventID.map { .init(eventID: $0, appearance: .immediate) }), ownUserID: roomProxy.ownUserID, isViewSourceEnabled: appSettings.viewSourceEnabled, + hideTimelineMedia: appSettings.hideTimelineMedia, bindings: .init(reactionsCollapsed: [:])), mediaProvider: mediaProvider) @@ -440,6 +441,10 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { appSettings.$viewSourceEnabled .weakAssign(to: \.state.isViewSourceEnabled, on: self) .store(in: &cancellables) + + appSettings.$hideTimelineMedia + .weakAssign(to: \.state.hideTimelineMedia, on: self) + .store(in: &cancellables) } private func updatePinnedEventIDs() async { diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift index b872a84973..c213ead505 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift @@ -15,6 +15,7 @@ struct ImageRoomTimelineView: View { var body: some View { TimelineStyler(timelineItem: timelineItem) { LoadableImage(mediaSource: source, + mediaType: .timelineItem, blurhash: timelineItem.content.blurhash, mediaProvider: context.mediaProvider) { placeholder @@ -35,14 +36,9 @@ struct ImageRoomTimelineView: View { } var placeholder: some View { - ZStack { - Rectangle() - .foregroundColor(timelineItem.isOutgoing ? .compound._bgBubbleOutgoing : .compound._bgBubbleIncoming) - .opacity(0.3) - - ProgressView(L10n.commonLoading) - .frame(maxWidth: .infinity) - } + Rectangle() + .foregroundColor(timelineItem.isOutgoing ? .compound._bgBubbleOutgoing : .compound._bgBubbleIncoming) + .opacity(0.3) } } diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift index 720d5c49f2..2d9a17f3c8 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift @@ -15,6 +15,7 @@ struct StickerRoomTimelineView: View { var body: some View { TimelineStyler(timelineItem: timelineItem) { LoadableImage(url: timelineItem.imageURL, + mediaType: .timelineItem, blurhash: timelineItem.blurhash, mediaProvider: context.mediaProvider) { placeholder @@ -27,14 +28,9 @@ struct StickerRoomTimelineView: View { } private var placeholder: some View { - ZStack { - Rectangle() - .foregroundColor(timelineItem.isOutgoing ? .compound._bgBubbleOutgoing : .compound._bgBubbleIncoming) - .opacity(0.3) - - ProgressView(L10n.commonLoading) - .frame(maxWidth: .infinity) - } + Rectangle() + .foregroundColor(timelineItem.isOutgoing ? .compound._bgBubbleOutgoing : .compound._bgBubbleIncoming) + .opacity(0.3) } } diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift index c681f0e3be..23bc73d589 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift @@ -26,6 +26,7 @@ struct VideoRoomTimelineView: View { var thumbnail: some View { if let thumbnailSource = timelineItem.content.thumbnailSource { LoadableImage(mediaSource: thumbnailSource, + mediaType: .timelineItem, blurhash: timelineItem.content.blurhash, mediaProvider: context.mediaProvider) { imageView in imageView @@ -47,14 +48,9 @@ struct VideoRoomTimelineView: View { } var placeholder: some View { - ZStack { - Rectangle() - .foregroundColor(timelineItem.isOutgoing ? .compound._bgBubbleOutgoing : .compound._bgBubbleIncoming) - .opacity(0.3) - - ProgressView(L10n.commonLoading) - .frame(maxWidth: .infinity) - } + Rectangle() + .foregroundColor(timelineItem.isOutgoing ? .compound._bgBubbleOutgoing : .compound._bgBubbleIncoming) + .opacity(0.3) } } diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineView.swift index 1090dbc56c..c9133b242e 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineView.swift @@ -60,6 +60,9 @@ struct TimelineView: UIViewControllerRepresentable { if tableViewController.focussedEvent != context.viewState.timelineViewState.focussedEvent { tableViewController.focussedEvent = context.viewState.timelineViewState.focussedEvent } + if tableViewController.hideTimelineMedia != context.viewState.hideTimelineMedia { + tableViewController.hideTimelineMedia = context.viewState.hideTimelineMedia + } if tableViewController.typingMembers.members != context.viewState.typingMembers { tableViewController.setTypingMembers(context.viewState.typingMembers) diff --git a/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift b/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift index 62cad3cd3f..820e880b81 100644 --- a/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift +++ b/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift @@ -247,6 +247,40 @@ enum RoomTimelineItemFixtures { senderDisplayName: index > 10 ? "Alice" : "Bob") } } + + static var mediaChunk: [RoomTimelineItemProtocol] { + [ + VideoRoomTimelineItem(id: .random, + timestamp: "10:47 am", + isOutgoing: false, + isEditable: false, + canBeRepliedTo: true, + isThreaded: false, + sender: .init(id: ""), + content: .init(body: "video", + duration: 100, + source: .init(url: .picturesDirectory, mimeType: nil), + thumbnailSource: .init(url: .picturesDirectory, mimeType: nil), + width: 1920, + height: 1080, + aspectRatio: 1.78, + blurhash: "KtI~70X5V?yss9oyrYs:t6")), + ImageRoomTimelineItem(id: .random, + timestamp: "10:47 am", + isOutgoing: false, + isEditable: false, + canBeRepliedTo: true, + isThreaded: false, + sender: .init(id: ""), + content: .init(body: "image", + source: .init(url: .picturesDirectory, mimeType: nil), + thumbnailSource: nil, + width: 5120, + height: 3412, + aspectRatio: 1.5, + blurhash: "KpE4oyayR5|GbHb];3j@of")) + ] + } } private extension TextRoomTimelineItem { @@ -260,9 +294,7 @@ private extension TextRoomTimelineItem { sender: .init(id: "", displayName: senderDisplayName), content: .init(body: text)) } -} - -private extension TextRoomTimelineItem { + func withReadReceipts(_ receipts: [ReadReceipt]) -> TextRoomTimelineItem { var newSelf = self newSelf.properties.orderedReadReceipts = receipts diff --git a/NSE/Sources/NotificationContentBuilder.swift b/NSE/Sources/NotificationContentBuilder.swift index ad01691b4d..f767a9538d 100644 --- a/NSE/Sources/NotificationContentBuilder.swift +++ b/NSE/Sources/NotificationContentBuilder.swift @@ -11,6 +11,7 @@ import UserNotifications struct NotificationContentBuilder { let messageEventStringBuilder: RoomMessageEventStringBuilder + let settings: CommonSettingsProtocol /// Process the given notification item proxy /// - Parameters: @@ -100,6 +101,8 @@ struct NotificationContentBuilder { let displayName = notificationItem.senderDisplayName ?? notificationItem.roomDisplayName notification.body = String(messageEventStringBuilder.buildAttributedString(for: messageType, senderDisplayName: displayName).characters) + guard !settings.hideTimelineMedia else { return notification } + switch messageType { case .image(content: let content): notification = await notification.addMediaAttachment(using: mediaProvider, diff --git a/NSE/Sources/NotificationServiceExtension.swift b/NSE/Sources/NotificationServiceExtension.swift index 5443c1ffc3..3ea1ed8a6c 100644 --- a/NSE/Sources/NotificationServiceExtension.swift +++ b/NSE/Sources/NotificationServiceExtension.swift @@ -33,7 +33,8 @@ import UserNotifications // database, logging, etc. are only ever setup once per *process* private let settings: CommonSettingsProtocol = AppSettings() -private let notificationContentBuilder = NotificationContentBuilder(messageEventStringBuilder: RoomMessageEventStringBuilder(attributedStringBuilder: AttributedStringBuilder(mentionBuilder: PlainMentionBuilder()), prefix: .none)) +private let notificationContentBuilder = NotificationContentBuilder(messageEventStringBuilder: RoomMessageEventStringBuilder(attributedStringBuilder: AttributedStringBuilder(mentionBuilder: PlainMentionBuilder()), prefix: .none), + settings: settings) private let keychainController = KeychainController(service: .sessions, accessGroup: InfoPlistReader.main.keychainAccessGroupIdentifier) diff --git a/PreviewTests/Sources/GeneratedPreviewTests.swift b/PreviewTests/Sources/GeneratedPreviewTests.swift index 47fb1f5988..5819975f1c 100644 --- a/PreviewTests/Sources/GeneratedPreviewTests.swift +++ b/PreviewTests/Sources/GeneratedPreviewTests.swift @@ -311,6 +311,12 @@ extension PreviewTests { } } + func test_loadableImage() { + for preview in LoadableImage_Previews._allPreviews { + assertSnapshots(matching: preview) + } + } + func test_locationMarkerView() { for preview in LocationMarkerView_Previews._allPreviews { assertSnapshots(matching: preview) diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loadableImage-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loadableImage-iPad-en-GB.1.png new file mode 100644 index 0000000000..a6d91419e9 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loadableImage-iPad-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d29fa1a636ab74e34bed4d8155c5801a586e1d93c7fb3da736df99941d0b374 +size 195721 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loadableImage-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loadableImage-iPad-pseudo.1.png new file mode 100644 index 0000000000..c9ad057634 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loadableImage-iPad-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dcc87dd75498a5eaf4033d3630228ee8998b25832714a3fb7586d421cd4a9600 +size 209012 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loadableImage-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loadableImage-iPhone-16-en-GB.1.png new file mode 100644 index 0000000000..a75077b460 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loadableImage-iPhone-16-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b6a5bcecdde9ae09281b3a14fe74640bdc3a49b00453394efa37ceb943ca9ef0 +size 182115 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loadableImage-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loadableImage-iPhone-16-pseudo.1.png new file mode 100644 index 0000000000..e8634a78ec --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loadableImage-iPhone-16-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:185a048522753d4c5175d6bd35ec98e463aeec8b4674b0e5ad7c2ef20a683001 +size 195492 From 8ba62f5ca085f8a34045c8399c7ceb437ce8e7c5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 10:16:14 +0100 Subject: [PATCH 031/114] chore(deps): update dependency fastlane to v2.224.0 (#3370) --- Gemfile.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8758696e1e..8e22eaab5e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -16,7 +16,7 @@ GEM artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.3.0) - aws-partitions (1.979.0) + aws-partitions (1.984.0) aws-sdk-core (3.209.1) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) @@ -25,7 +25,7 @@ GEM aws-sdk-kms (1.94.0) aws-sdk-core (~> 3, >= 3.207.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.166.0) + aws-sdk-s3 (1.167.0) aws-sdk-core (~> 3, >= 3.207.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) @@ -44,7 +44,7 @@ GEM domain_name (0.6.20240107) dotenv (2.8.1) emoji_regex (3.2.3) - excon (0.111.0) + excon (0.112.0) faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) @@ -74,7 +74,7 @@ GEM faraday_middleware (1.2.1) faraday (~> 1.0) fastimage (2.3.1) - fastlane (2.223.1) + fastlane (2.224.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -167,7 +167,7 @@ GEM httpclient (2.8.3) jmespath (1.6.2) json (2.7.2) - jwt (2.9.1) + jwt (2.9.3) base64 mime-types (3.5.2) mime-types-data (~> 3.2015) @@ -195,7 +195,7 @@ GEM mime-types (>= 1.16, < 4.0) netrc (~> 0.8) retriable (3.1.2) - rexml (3.3.7) + rexml (3.3.8) rouge (2.0.7) ruby2_keywords (0.0.5) rubyzip (2.3.2) @@ -223,13 +223,13 @@ GEM xcode-install (2.8.1) claide (>= 0.9.1) fastlane (>= 2.1.0, < 3.0.0) - xcodeproj (1.25.0) + xcodeproj (1.25.1) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.3.0) - rexml (>= 3.3.2, < 4.0) + rexml (>= 3.3.6, < 4.0) xcpretty (0.3.0) rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.1) From ac02b82d56b197c5a0c056878c903919c65fc53b Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 7 Oct 2024 10:50:03 +0100 Subject: [PATCH 032/114] Record missing snapshot. (#3374) --- .../Application/authenticationFlow-1-iPhone-16-en-GB.UI.png | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPhone-16-en-GB.UI.png diff --git a/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPhone-16-en-GB.UI.png new file mode 100644 index 0000000000..0dacff06ef --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPhone-16-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:120cd74cb96ee9bf2615fff542d0d8a688448a91271ce6d50540314cb7795092 +size 254990 From 70652ed7be082bcd9ec80b012fcb309f1e8e78bd Mon Sep 17 00:00:00 2001 From: ElementRobot Date: Mon, 7 Oct 2024 04:51:59 -0500 Subject: [PATCH 033/114] Translations update (#3371) --- .../be.lproj/Localizable.strings | 51 +- .../bg.lproj/Localizable.strings | 53 +- .../cs.lproj/Localizable.strings | 51 +- .../de.lproj/Localizable.strings | 71 +- .../el.lproj/Localizable.strings | 57 +- .../en.lproj/Localizable.strings | 48 +- .../es.lproj/Localizable.strings | 53 +- .../et.lproj/Localizable.strings | 51 +- .../fa.lproj/Localizable.strings | 1019 +++++++++++++++++ .../Localizations/fa.lproj/SAS.strings | 64 ++ .../fr.lproj/Localizable.strings | 57 +- .../hu.lproj/Localizable.strings | 53 +- .../id.lproj/Localizable.strings | 53 +- .../it.lproj/Localizable.strings | 139 +-- .../ka.lproj/Localizable.strings | 53 +- .../nl.lproj/Localizable.strings | 53 +- .../pl.lproj/Localizable.strings | 149 +-- .../pl.lproj/Localizable.stringsdict | 8 +- .../pt-BR.lproj/Localizable.strings | 53 +- .../pt.lproj/Localizable.strings | 149 +-- .../pt.lproj/Localizable.stringsdict | 4 +- .../ro.lproj/Localizable.strings | 53 +- .../ru.lproj/Localizable.strings | 51 +- .../sk.lproj/Localizable.strings | 51 +- .../sv.lproj/Localizable.strings | 51 +- .../uk.lproj/Localizable.strings | 51 +- .../uz.lproj/Localizable.strings | 53 +- .../zh-Hans.lproj/Localizable.strings | 51 +- .../zh-Hant-TW.lproj/Localizable.strings | 53 +- ElementX/Sources/Generated/Strings.swift | 8 +- ...est_encryptionResetScreen-iPad-en-GB.1.png | 4 +- ...st_encryptionResetScreen-iPad-pseudo.1.png | 4 +- ...ncryptionResetScreen-iPhone-16-en-GB.1.png | 4 +- ...cryptionResetScreen-iPhone-16-pseudo.1.png | 4 +- 34 files changed, 1946 insertions(+), 781 deletions(-) create mode 100644 ElementX/Resources/Localizations/fa.lproj/Localizable.strings create mode 100644 ElementX/Resources/Localizations/fa.lproj/SAS.strings diff --git a/ElementX/Resources/Localizations/be.lproj/Localizable.strings b/ElementX/Resources/Localizations/be.lproj/Localizable.strings index 4660c8f099..1017183541 100644 --- a/ElementX/Resources/Localizations/be.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/be.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Паўза"; "a11y_pin_field" = "Поле PIN-кода"; "a11y_play" = "Прайграць"; -"a11y_poll" = "Апытанне"; "a11y_poll_end" = "Апытанне скончана"; "a11y_react_with" = "Рэагаваць з %1$@"; "a11y_react_with_other_emojis" = "Рэагаваць з іншымі эмодзі"; @@ -41,6 +40,7 @@ "action_create" = "Стварыць"; "action_create_a_room" = "Стварыце пакой"; "action_deactivate" = "Дэактываваць"; +"action_deactivate_account" = "Дэактываваць уліковы запіс"; "action_decline" = "Адхіліць"; "action_delete_poll" = "Выдаліць апытанне"; "action_disable" = "Адключыць"; @@ -64,6 +64,7 @@ "action_leave" = "Пакінуць"; "action_leave_conversation" = "Пакінуць размову"; "action_leave_room" = "Пакінуць пакой"; +"action_load_more" = "Загрузіць больш"; "action_manage_account" = "Кіраванне ўліковым запісам"; "action_manage_devices" = "Кіраванне прыладамі"; "action_message" = "Паведамленне"; @@ -93,6 +94,7 @@ "action_send_message" = "Адправіць паведамленне"; "action_share" = "Падзяліцца"; "action_share_link" = "Абагуліць спасылку"; +"action_show" = "Show"; "action_sign_in_again" = "Увайдзіце яшчэ раз"; "action_signout" = "Выйсці"; "action_signout_anyway" = "Усё роўна выйсці"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "Прагляд у хроніцы"; "action_view_source" = "Прагляд зыходнага кода"; "action_yes" = "Так"; -"action.load_more" = "Загрузіць больш"; -"action_deactivate_account" = "Дэактываваць уліковы запіс"; "banner_migrate_to_native_sliding_sync_action" = "Выйсці і абнавіць"; "banner_migrate_to_native_sliding_sync_description" = "Ваш сервер зараз падтрымлівае новы, хутчэйшы пратакол. Выйдзіце з сістэмы і зноў увайдзіце, каб абнавіць яе. Гэта дапаможа вам пазбегнуць прымусовага выхаду з сістэмы, калі стары пратакол будзе пазней выдалены."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Ваш хатні сервер больш не падтрымлівае стары пратакол. Калі ласка, выйдзіце і ўвайдзіце зноў, каб працягнуць выкарыстанне праграмы."; @@ -162,6 +162,7 @@ "common_modern" = "Сучасны"; "common_mute" = "Адкл. гук"; "common_no_results" = "Вынікаў няма"; +"common_no_room_name" = "Няма назвы пакоя"; "common_offline" = "Па-за сеткай"; "common_optic_id_ios" = "Optic ID"; "common_or" = "або"; @@ -170,6 +171,8 @@ "common_permalink" = "Пастаянная спасылка"; "common_permission" = "Дазвол"; "common_please_wait" = "Калі ласка, пачакайце…"; +"common_poll_end_confirmation" = "Вы ўпэўнены, што хочаце скончыць гэтае апытанне?"; +"common_poll_summary" = "Апытанне: %1$@"; "common_poll_total_votes" = "Усяго галасоў: %1$@"; "common_poll_undisclosed_text" = "Вынікі будуць паказаны пасля завяршэння апытання"; "common_privacy_policy" = "Палітыка прыватнасці"; @@ -200,6 +203,7 @@ "common_settings" = "Налады"; "common_shared_location" = "Абагулена месцазнаходжанне"; "common_signing_out" = "Выхад"; +"common_something_went_wrong" = "Нешта пайшло не так"; "common_starting_chat" = "Пачатак чата…"; "common_sticker" = "Стыкер"; "common_success" = "Поспех"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "Пра што гэты пакой?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Немагчыма расшыфраваць"; +"common_unable_to_decrypt_no_access" = "У вас няма доступу да гэтага паведамлення"; "common_unable_to_invite_message" = "Не ўдалося адправіць запрашэнні аднаму або некалькім карыстальнікам."; "common_unable_to_invite_title" = "Немагчыма адправіць запрашэнне(я)"; "common_unlock" = "Разблакіраваць"; @@ -221,24 +226,21 @@ "common_username" = "Імя карыстальніка"; "common_verification_cancelled" = "Праверка адменена"; "common_verification_complete" = "Праверка завершана"; +"common_verify_device" = "Праверце прыладу"; "common_video" = "Відэа"; "common_voice_message" = "Галасавое паведамленне"; "common_waiting" = "Чакаем…"; "common_waiting_for_decryption_key" = "Чакаю гэта паведамленне"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "Не паказваць гэта зноў"; "common.open_source_licenses" = "Ліцэнзіі з адкрытым зыходным кодам"; "common.pinned" = "Замацаваны"; "common.send_to" = "Адправіць"; "common.you" = "Вы"; -"common_no_room_name" = "Няма назвы пакоя"; -"common_poll_end_confirmation" = "Вы ўпэўнены, што хочаце скончыць гэтае апытанне?"; -"common_poll_summary" = "Апытанне: %1$@"; -"common_something_went_wrong" = "Нешта пайшло не так"; -"common_unable_to_decrypt_no_access" = "У вас няма доступу да гэтага паведамлення"; -"common_verify_device" = "Праверце прыладу"; "confirm_recovery_key_banner_message" = "Ваша рэзервовая копія чата зараз не сінхранізавана. Вам трэба пацвердзіць ключ аднаўлення, каб захаваць доступ да рэзервовай копіі чата."; "confirm_recovery_key_banner_title" = "Увядзіце ключ аднаўлення"; "crash_detection_dialog_content" = "Пры апошнім выкарыстанні %1$@ адбыўся збой. Хочаце падзяліцца справаздачай аб збоі?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "Каб дазволіць праграме выкарыстоўваць камеру, дайце дазвол у наладах сістэмы."; "dialog_permission_generic" = "Калі ласка, дайце дазвол у наладах сістэмы."; "dialog_permission_location_description_ios" = "Дайце доступ у Наладах -> Месца знаходжання."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "Ціхія апавяшчэнні"; "notification_incoming_call" = "Уваходны званок"; "notification_inline_reply_failed" = "** Не атрымалася даслаць - калі ласка, адкрыйце пакой"; -"notification_invitation_action_reject" = "Адхіліць"; "notification_invite_body" = "Запрасіў(-ла) вас у чат"; "notification_invite_body_with_sender" = "%1$@ запрасіў(-ла) вас у чат"; "notification_mentioned_you_body" = "Згадаў(-ла) вас: %1$@"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "Адрас пазначаны няправільна, пераканайцеся, што вы ўказалі пратакол (http/https) і правільны адрас."; "screen_pinned_timeline_empty_state_description" = "Націсніце на паведамленне і абярыце «%1$@ », каб уключыць сюды."; "screen_pinned_timeline_empty_state_headline" = "Замацуеце важныя паведамленні, каб іх можна было лёгка знайсці"; -"screen_pinned_timeline_screen_title_empty" = "Замацаваныя паведамленні"; "screen_reset_encryption_password_error" = "Адбылася невядомая памылка. Калі ласка, праверце правільнасць пароля вашага ўліковага запісу і паўтарыце спробу."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Адклікаць праверку і адправіць"; "screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "Змяніць правайдара ўліковага запісу"; "screen_account_provider_form_hint" = "Адрас хатняга сервера"; "screen_account_provider_form_notice" = "Увядзіце пошукавы запыт або адрас дамена."; "screen_account_provider_form_subtitle" = "Пошук кампаніі, супольнасці або прыватнага сервера."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "Рэзервовае капіраванне гарантуе, што вы не страціце сваю гісторыю паведамленняў. %1$@."; "screen_chat_backup_key_backup_title" = "Рэзервовая копія"; "screen_chat_backup_recovery_action_change" = "Змяніць ключ аднаўлення"; -"screen_chat_backup_recovery_action_confirm" = "Увядзіце ключ аднаўлення"; "screen_chat_backup_recovery_action_confirm_description" = "Ваша рэзервовая копія чата зараз не сінхранізавана."; "screen_chat_backup_recovery_action_setup" = "Наладзьце аднаўленне"; "screen_chat_backup_recovery_action_setup_description" = "Атрымайце доступ да зашыфраваных паведамленняў, калі вы страціце ўсе свае прылады або выйдзеце з сістэмы %1$@ усюды."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Паказаць вынікі толькі пасля заканчэння апытання"; "screen_create_poll_anonymous_headline" = "Схаваць галасы"; "screen_create_poll_answer_hint" = "Варыянт %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Вашы змены не будуць захаваны"; "screen_create_poll_cancel_confirmation_title_ios" = "Адмяніць апытанне"; "screen_create_poll_question_desc" = "Пытанне або тэма"; "screen_create_poll_question_hint" = "Пра што апытанне?"; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Групавыя чаты"; "screen_notification_settings_invite_for_me_label" = "Запрашэнні"; "screen_notification_settings_mentions_only_disclaimer" = "Ваш хатні сервер не падтрымлівае гэтую опцыю ў зашыфраваных пакоях, вы можаце не атрымаць апавяшчэнне ў некаторых пакоях."; -"screen_notification_settings_mentions_section_title" = "Згадванні"; "screen_notification_settings_mode_all" = "Усе"; "screen_notification_settings_mode_mentions" = "Згадванні"; "screen_notification_settings_notification_section_title" = "Апавясціць мяне"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Увесці..."; "screen_recovery_key_confirm_lost_recovery_key" = "Страцілі ключ аднаўлення?"; "screen_recovery_key_confirm_success" = "Ключ аднаўлення пацверджаны"; -"screen_recovery_key_confirm_title" = "Увядзіце ключ аднаўлення"; "screen_recovery_key_copied_to_clipboard" = "Ключ аднаўлення скапіраваны"; "screen_recovery_key_generating_key" = "Стварэнне…"; "screen_recovery_key_save_action" = "Захаваць ключ аднаўлення"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Так, скінуць зараз"; "screen_reset_encryption_confirmation_alert_subtitle" = "Гэты працэс незваротны."; "screen_reset_encryption_confirmation_alert_title" = "Вы ўпэўнены, што хочаце скінуць шыфраванне?"; -"screen_reset_encryption_password_placeholder" = "Увод..."; "screen_reset_encryption_password_subtitle" = "Пацвердзіце, што вы хочаце скінуць шыфраванне"; "screen_reset_encryption_password_title" = "Каб працягнуць, увядзіце пароль уліковага запісу"; "screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Адміністратары аўтаматычна маюць права мадэратара"; "screen_room_change_role_moderators_title" = "Рэдагаваць мадэратараў"; "screen_room_change_role_unsaved_changes_description" = "У вас ёсць незахаваныя змены."; -"screen_room_change_role_unsaved_changes_title" = "Захаваць змены?"; "screen_room_details_add_topic_title" = "Дадаць тэму"; "screen_room_details_already_a_member" = "Ужо ўдзельнік"; "screen_room_details_already_invited" = "Ужо запрасілі"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Не ўдалося ўключыць гук у гэтым пакоі. Паўтарыце спробу."; "screen_room_details_notification_mode_custom" = "Уласныя"; "screen_room_details_notification_mode_default" = "Стандартныя"; -"screen_room_details_notification_title" = "Апавяшчэнні"; "screen_room_details_share_room_title" = "Падзяліцца пакоем"; "screen_room_details_title" = "Інфармацыя аб пакоі"; "screen_room_details_updating_room" = "Ідзе абнаўленне пакоя…"; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Блакіроўка %1$@"; "screen_room_member_list_manage_member_ban" = "Выдаліць і заблакіраваць удзельніка"; "screen_room_member_list_manage_member_remove" = "Выдаліць удзельніка з пакоя"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Выдаліць і заблакіраваць удзельніка"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Толькі выдаліць удзельніка"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Выдаліць удзельніка і забараніць далучацца ў будучыні?"; "screen_room_member_list_manage_member_unban_action" = "Разблакіраваць"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Пазначыць як прачытанае"; "screen_roomlist_mark_as_unread" = "Пазначыць як непрачытанае"; "screen_roomlist_room_directory_button_title" = "Праглядзець усе пакоі"; -"screen_server_confirmation_change_server" = "Змяніць правайдара ўліковага запісу"; "screen_server_confirmation_message_login_element_dot_io" = "Прыватны сервер для супрацоўнікаў Element."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix - гэта адкрытая сетка для бяспечнай, дэцэнтралізаванай сувязі."; "screen_server_confirmation_message_register" = "Тут будуць захоўвацца вашыя размовы - сапраўды гэтак жа, як вы выкарыстоўваеце паштовага правайдара для захоўвання сваіх лістоў."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Вы збіраецеся выйсці з апошняга сеанса. Калі вы выйдзеце з сістэмы зараз, вы страціце доступ да зашыфраваных паведамленняў."; "screen_signout_key_backup_disabled_title" = "Вы адключылі рэзервовае капіраванне"; "screen_signout_key_backup_offline_subtitle" = "Вашы ключы ўсё яшчэ захоўваліся, калі вы выйшлі з сеткі. Паўторна падключыцеся, каб можна было стварыць рэзервовую копію вашых ключоў перад выхадам."; -"screen_signout_key_backup_offline_title" = "Рэзервовае капіраванне ключоў усё яшчэ працягваецца"; "screen_signout_key_backup_ongoing_subtitle" = "Калі ласка, дачакайцеся завяршэння працэсу, перш чым выходзіць з сістэмы."; "screen_signout_key_backup_ongoing_title" = "Вашы ключы ўсё яшчэ ствараюцца"; "screen_signout_recovery_disabled_subtitle" = "Вы збіраецеся выйсці з апошняга сеанса. Калі вы выйдзеце з сістэмы зараз, вы страціце доступ да зашыфраваных паведамленняў."; "screen_signout_recovery_disabled_title" = "Аднаўленне не наладжана"; "screen_signout_save_recovery_key_subtitle" = "Вы збіраецеся выйсці з апошняга сеанса. Калі вы выйдзеце з сістэмы зараз, вы страціце доступ да зашыфраваных паведамленняў."; -"screen_signout_save_recovery_key_title" = "Вы захавалі свой ключ аднаўлення?"; "screen_start_chat_error_starting_chat" = "Пры спробе пачаць чат адбылася памылка"; "screen_view_location_title" = "Месцазнаходжанне"; "screen_welcome_bullet_1" = "Званкі, апытанні, пошук і многае іншае будзе дададзена пазней у гэтым годзе."; @@ -920,7 +908,6 @@ "test_language_identifier" = "be"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Выпраўленне непаладак"; -"troubleshoot_notifications_entry_point_title" = "Выпраўленне непаладак з апавяшчэннямі"; "troubleshoot_notifications_screen_action" = "Запусціць тэсты"; "troubleshoot_notifications_screen_action_again" = "Запусціце тэсты яшчэ раз"; "troubleshoot_notifications_screen_failure" = "Некаторыя тэсты не ўдаліся. Калі ласка, праглядзіце дэталі."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Пераканайцеся, што размеркавальнікі UnifiedPush даступныя."; "troubleshoot_notifications_test_unified_push_failure" = "Размеркавальнікі не знойдзены."; "troubleshoot_notifications_test_unified_push_title" = "Праверыць UnifiedPush"; +"a11y_poll" = "Апытанне"; "dialog_title_error" = "Памылка"; "dialog_title_success" = "Поспех"; "notification_fallback_content" = "Апавяшчэнне"; "notification_invitation_action_join" = "Далучыцца"; +"notification_invitation_action_reject" = "Адхіліць"; "notification_room_action_mark_as_read" = "Пазначыць як прачытанае"; "notification_room_action_quick_reply" = "Хуткі адказ"; +"screen_pinned_timeline_screen_title_empty" = "Замацаваныя паведамленні"; "screen_room_mentions_at_room_title" = "Усе"; +"screen_account_provider_change" = "Змяніць правайдара ўліковага запісу"; "screen_account_provider_signin_subtitle" = "Тут будуць захоўвацца вашыя размовы - сапраўды гэтак жа, як вы выкарыстоўваеце паштовага правайдара для захоўвання сваіх лістоў."; "screen_account_provider_signup_subtitle" = "Тут будуць захоўвацца вашыя размовы - сапраўды гэтак жа, як вы выкарыстоўваеце паштовага правайдара для захоўвання сваіх лістоў."; "screen_analytics_settings_help_us_improve" = "Даваць ананімныя дадзеныя аб выкарыстанні, каб дапамагчы нам выявіць праблемы."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Вы зноў зможаце ўбачыць усе паведамленні."; "screen_blocked_users_unblock_alert_title" = "Разблакіраваць карыстальніка"; "screen_bug_report_rash_logs_alert_title" = "Пры апошнім выкарыстанні %1$@ адбыўся збой. Хочаце падзяліцца справаздачай аб збоі?"; +"screen_chat_backup_recovery_action_confirm" = "Увядзіце ключ аднаўлення"; +"screen_create_poll_cancel_confirmation_content_ios" = "Вашы змены не будуць захаваны"; "screen_create_room_add_people_title" = "Запрасіць карыстальнікаў"; "screen_create_room_room_name_label" = "Назва пакоя"; "screen_create_room_title" = "Стварыце пакой"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Рэдагаваць апытанне"; "screen_identity_use_another_device" = "Выкарыстоўвайце іншую прыладу"; "screen_login_subtitle" = "Matrix - гэта адкрытая сетка для бяспечнай, дэцэнтралізаванай сувязі."; +"screen_notification_settings_mentions_section_title" = "Згадванні"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Паўтарыць спробу"; +"screen_recovery_key_confirm_title" = "Увядзіце ключ аднаўлення"; "screen_report_content_block_user" = "Заблакіраваць карыстальніка"; +"screen_reset_encryption_password_placeholder" = "Увесці..."; "screen_room_attachment_source_camera_photo" = "Зрабіць фота"; "screen_room_change_permissions_everyone" = "Усе"; "screen_room_change_permissions_member_moderation" = "Мадэрацыя ўдзельнікаў"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Адміністратары"; "screen_room_change_role_section_moderators" = "Мадэратары"; "screen_room_change_role_section_users" = "Удзельнікі"; +"screen_room_change_role_unsaved_changes_title" = "Захаваць змены?"; "screen_room_details_invite_people_title" = "Запрасіць карыстальнікаў"; "screen_room_details_leave_conversation_title" = "Пакінуць размову"; "screen_room_details_leave_room_title" = "Пакінуць пакой"; +"screen_room_details_notification_title" = "Апавяшчэнні"; "screen_room_details_roles_and_permissions" = "Ролі і дазволы"; "screen_room_details_room_name_label" = "Назва пакоя"; "screen_room_details_security_title" = "Бяспека"; "screen_room_details_topic_title" = "Тэма"; "screen_room_error_failed_processing_media" = "Не атрымалася апрацаваць медыяфайл для загрузкі, паспрабуйце яшчэ раз."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Выдаліць і заблакіраваць удзельніка"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Толькі згадванні і ключавыя словы"; "screen_roomlist_filter_people" = "Людзі"; +"screen_server_confirmation_change_server" = "Змяніць правайдара ўліковага запісу"; "screen_signout_confirmation_dialog_submit" = "Выйсці"; "screen_signout_confirmation_dialog_title" = "Выйсці"; +"screen_signout_key_backup_offline_title" = "Вашы ключы ўсё яшчэ ствараюцца"; "screen_signout_preference_item" = "Выйсці"; +"screen_signout_save_recovery_key_title" = "Вы захавалі свой ключ аднаўлення?"; +"troubleshoot_notifications_entry_point_title" = "Выпраўленне непаладак з апавяшчэннямі"; diff --git a/ElementX/Resources/Localizations/bg.lproj/Localizable.strings b/ElementX/Resources/Localizations/bg.lproj/Localizable.strings index 1bc78b28e7..b459c6d4da 100644 --- a/ElementX/Resources/Localizations/bg.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/bg.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Пауза"; "a11y_pin_field" = "PIN поле"; "a11y_play" = "Пускане"; -"a11y_poll" = "Анкета"; "a11y_poll_end" = "Приключила анкета"; "a11y_react_with" = "Реакция с %1$@"; "a11y_react_with_other_emojis" = "Реакция с други емоджита"; @@ -41,6 +40,7 @@ "action_create" = "Създаване"; "action_create_a_room" = "Създаване на стая"; "action_deactivate" = "Deactivate"; +"action_deactivate_account" = "Deactivate account"; "action_decline" = "Отхвърляне"; "action_delete_poll" = "Изтриване на анкетата"; "action_disable" = "Деактивиране"; @@ -64,6 +64,7 @@ "action_leave" = "Напускане"; "action_leave_conversation" = "Напускане на разговора"; "action_leave_room" = "Напускане на стаята"; +"action_load_more" = "Зареждане на още"; "action_manage_account" = "Управление на профила"; "action_manage_devices" = "Управление на устройства"; "action_message" = "Message"; @@ -93,6 +94,7 @@ "action_send_message" = "Изпращане на съобщение"; "action_share" = "Споделяне"; "action_share_link" = "Споделяне на връзката"; +"action_show" = "Show"; "action_sign_in_again" = "Влизане отново"; "action_signout" = "Изход"; "action_signout_anyway" = "Излизане въпреки това"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "View in timeline"; "action_view_source" = "Преглед на източника"; "action_yes" = "Да"; -"action.load_more" = "Зареждане на още"; -"action_deactivate_account" = "Deactivate account"; "banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; @@ -162,6 +162,7 @@ "common_modern" = "Модерно"; "common_mute" = "Заглушаване"; "common_no_results" = "Няма резултати"; +"common_no_room_name" = "No room name"; "common_offline" = "Офлайн"; "common_optic_id_ios" = "Optic ID"; "common_or" = "или"; @@ -170,6 +171,8 @@ "common_permalink" = "Постоянна връзка"; "common_permission" = "Разрешение"; "common_please_wait" = "Please wait…"; +"common_poll_end_confirmation" = "Сигурни ли сте, че искате да приключите тази анкета?"; +"common_poll_summary" = "Анкета: %1$@"; "common_poll_total_votes" = "Общо гласове: %1$@"; "common_poll_undisclosed_text" = "Резултатите ще се покажат след приключване на анкетата"; "common_privacy_policy" = "Политика за поверителност"; @@ -200,6 +203,7 @@ "common_settings" = "Настройки"; "common_shared_location" = "Споделено местоположение"; "common_signing_out" = "Излизате"; +"common_something_went_wrong" = "Something went wrong"; "common_starting_chat" = "Започване на чат…"; "common_sticker" = "Sticker"; "common_success" = "Успешно"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "За какво се отнася тази стая?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Не може да се разшифрова"; +"common_unable_to_decrypt_no_access" = "You don't have access to this message"; "common_unable_to_invite_message" = "Invites couldn't be sent to one or more users."; "common_unable_to_invite_title" = "Не може да се изпрати покана(и)"; "common_unlock" = "Отключване"; @@ -221,24 +226,21 @@ "common_username" = "Потребителско име"; "common_verification_cancelled" = "Потвърждаването е отменено"; "common_verification_complete" = "Потвърждаването е завършено"; +"common_verify_device" = "Потвърждаване на устройството"; "common_video" = "Видео"; "common_voice_message" = "Гласово съобщение"; "common_waiting" = "Waiting…"; "common_waiting_for_decryption_key" = "В очакване на това съобщение"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "Do not show this again"; "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "Send to"; "common.you" = "You"; -"common_no_room_name" = "No room name"; -"common_poll_end_confirmation" = "Сигурни ли сте, че искате да приключите тази анкета?"; -"common_poll_summary" = "Анкета: %1$@"; -"common_something_went_wrong" = "Something went wrong"; -"common_unable_to_decrypt_no_access" = "You don't have access to this message"; -"common_verify_device" = "Потвърждаване на устройството"; "confirm_recovery_key_banner_message" = "Резервното копие на чатовете ви в момента не е синхронизирано. Въведете ключа си за възстановяване, за да потвърдите достъпа до резервното копие на чатовете си."; "confirm_recovery_key_banner_title" = "Потвърдете ключа си за възстановяване"; "crash_detection_dialog_content" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "In order to let the application use the camera, please grant the permission in the system settings."; "dialog_permission_generic" = "Please grant the permission in the system settings."; "dialog_permission_location_description_ios" = "Grant access in Settings -> Location."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "Безшумни известия"; "notification_incoming_call" = "Incoming call"; "notification_inline_reply_failed" = "** Неуспешно изпращане - моля, отворете стаята"; -"notification_invitation_action_reject" = "Reject"; "notification_invite_body" = "Invited you to chat"; "notification_invite_body_with_sender" = "%1$@ invited you to chat"; "notification_mentioned_you_body" = "Ви спомена: %1$@"; @@ -335,7 +336,6 @@ "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_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; -"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; "screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "Промяна на доставчика на акаунт"; "screen_account_provider_form_hint" = "Homeserver address"; "screen_account_provider_form_notice" = "Въведете термин за търсене или адрес на домейн."; "screen_account_provider_form_subtitle" = "Потърсете компания, общност или частен сървър."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "Резервното копие гарантира, че няма да загубите хронологията на съобщенията си. %1$@."; "screen_chat_backup_key_backup_title" = "Резервно копие"; "screen_chat_backup_recovery_action_change" = "Промяна на ключа за възстановяване"; -"screen_chat_backup_recovery_action_confirm" = "Потвърждаване на ключа за възстановяване"; "screen_chat_backup_recovery_action_confirm_description" = "Резервното копие на чатовете ви в момента не е синхронизирано."; "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."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Показване на резултатите само след приключване на анкетата"; "screen_create_poll_anonymous_headline" = "Скриване на гласовете"; "screen_create_poll_answer_hint" = "Опция %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Промените ви няма да бъдат запазени"; "screen_create_poll_cancel_confirmation_title_ios" = "Cancel Poll"; "screen_create_poll_question_desc" = "Въпрос или тема"; "screen_create_poll_question_hint" = "За какво се отнася анкетата?"; @@ -480,7 +477,7 @@ "screen_edit_profile_updating_details" = "Обновяване на профила…"; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history"; +"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Групови чатове"; "screen_notification_settings_invite_for_me_label" = "Покани"; "screen_notification_settings_mentions_only_disclaimer" = "Your homeserver does not support this option in encrypted rooms, you may not get notified in some rooms."; -"screen_notification_settings_mentions_section_title" = "Споменавания"; "screen_notification_settings_mode_all" = "All"; "screen_notification_settings_mode_mentions" = "Споменавания"; "screen_notification_settings_notification_section_title" = "Да бъда известяван за"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Въведете…"; "screen_recovery_key_confirm_lost_recovery_key" = "Lost your recovery key?"; "screen_recovery_key_confirm_success" = "Ключът за възстановяване е потвърден"; -"screen_recovery_key_confirm_title" = "Потвърдете ключа си за възстановяване"; "screen_recovery_key_copied_to_clipboard" = "Копиран ключ за възстановяване"; "screen_recovery_key_generating_key" = "Generating…"; "screen_recovery_key_save_action" = "Запазване на ключа за възстановяване"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Yes, reset now"; "screen_reset_encryption_confirmation_alert_subtitle" = "This process is irreversible."; "screen_reset_encryption_confirmation_alert_title" = "Are you sure you want to reset your identity?"; -"screen_reset_encryption_password_placeholder" = "Enter…"; "screen_reset_encryption_password_subtitle" = "Confirm that you want to reset your identity."; "screen_reset_encryption_password_title" = "Enter your account password to continue"; "screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Admins automatically have moderator privileges"; "screen_room_change_role_moderators_title" = "Edit Moderators"; "screen_room_change_role_unsaved_changes_description" = "You have unsaved changes."; -"screen_room_change_role_unsaved_changes_title" = "Save changes?"; "screen_room_details_add_topic_title" = "Добавяне на тема"; "screen_room_details_already_a_member" = "Вече е член"; "screen_room_details_already_invited" = "Вече е бил поканен"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Failed unmuting this room, please try again."; "screen_room_details_notification_mode_custom" = "Персонализирани"; "screen_room_details_notification_mode_default" = "По подразбиране"; -"screen_room_details_notification_title" = "Известия"; "screen_room_details_share_room_title" = "Споделяне на стаята"; "screen_room_details_title" = "Room info"; "screen_room_details_updating_room" = "Обновяване на стаята…"; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Banning %1$@"; "screen_room_member_list_manage_member_ban" = "Remove and ban member"; "screen_room_member_list_manage_member_remove" = "Remove from room"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Remove and ban member"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Only remove member"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Remove member and ban from joining in the future?"; "screen_room_member_list_manage_member_unban_action" = "Unban"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Отбелязване като прочетено"; "screen_roomlist_mark_as_unread" = "Отбелязване като непрочетено"; "screen_roomlist_room_directory_button_title" = "Browse all rooms"; -"screen_server_confirmation_change_server" = "Промяна на доставчика на акаунт"; "screen_server_confirmation_message_login_element_dot_io" = "A private server for Element employees."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix е отворена мрежа за сигурна, децентрализирана комуникация."; "screen_server_confirmation_message_register" = "Това е мястото, където ще живеят вашите разговори — точно както бихте използвали имейл доставчик, за да съхранявате вашите имейли."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "You are about to sign out of your last session. If you sign out now, you will lose access to your encrypted messages."; "screen_signout_key_backup_disabled_title" = "You have turned off backup"; "screen_signout_key_backup_offline_subtitle" = "Your keys were still being backed up when you went offline. Reconnect so that your keys can be backed up before signing out."; -"screen_signout_key_backup_offline_title" = "Your keys are still being backed up"; "screen_signout_key_backup_ongoing_subtitle" = "Please wait for this to complete before signing out."; "screen_signout_key_backup_ongoing_title" = "Your keys are still being backed up"; "screen_signout_recovery_disabled_subtitle" = "You are about to sign out of your last session. If you sign out now, you'll lose access to your encrypted messages."; "screen_signout_recovery_disabled_title" = "Recovery not set up"; "screen_signout_save_recovery_key_subtitle" = "You are about to sign out of your last session. If you sign out now, you might lose access to your encrypted messages."; -"screen_signout_save_recovery_key_title" = "Have you saved your recovery key?"; "screen_start_chat_error_starting_chat" = "An error occurred when trying to start a chat"; "screen_view_location_title" = "Местоположение"; "screen_welcome_bullet_1" = "Calls, polls, search and more will be added later this year."; @@ -920,7 +908,6 @@ "test_language_identifier" = "bg"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Troubleshoot"; -"troubleshoot_notifications_entry_point_title" = "Troubleshoot notifications"; "troubleshoot_notifications_screen_action" = "Run tests"; "troubleshoot_notifications_screen_action_again" = "Run tests again"; "troubleshoot_notifications_screen_failure" = "Some tests failed. Please check the details."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Ensure that UnifiedPush distributors are available."; "troubleshoot_notifications_test_unified_push_failure" = "No push distributors found."; "troubleshoot_notifications_test_unified_push_title" = "Check UnifiedPush"; +"a11y_poll" = "Анкета"; "dialog_title_error" = "Грешка"; "dialog_title_success" = "Успешно"; "notification_fallback_content" = "Известие"; "notification_invitation_action_join" = "Присъединяване"; +"notification_invitation_action_reject" = "Reject"; "notification_room_action_mark_as_read" = "Отбелязване като прочетено"; "notification_room_action_quick_reply" = "Бърз отговор"; +"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_room_mentions_at_room_title" = "Everyone"; +"screen_account_provider_change" = "Промяна на доставчика на акаунт"; "screen_account_provider_signin_subtitle" = "Това е мястото, където ще живеят вашите разговори — точно както бихте използвали имейл доставчик, за да съхранявате вашите имейли."; "screen_account_provider_signup_subtitle" = "Това е мястото, където ще живеят вашите разговори — точно както бихте използвали имейл доставчик, за да съхранявате вашите имейли."; "screen_analytics_settings_help_us_improve" = "Споделяне на анонимни данни за използване, за да ни помогнете да идентифицираме проблеми."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "You'll be able to see all messages from them again."; "screen_blocked_users_unblock_alert_title" = "Отблокиране на потребителя"; "screen_bug_report_rash_logs_alert_title" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; +"screen_chat_backup_recovery_action_confirm" = "Въвеждане на ключ за възстановяване"; +"screen_create_poll_cancel_confirmation_content_ios" = "Your changes won’t be saved"; "screen_create_room_add_people_title" = "Поканване на хора"; "screen_create_room_room_name_label" = "Име на стаята"; "screen_create_room_title" = "Създаване на стая"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Редактиране на анкетата"; "screen_identity_use_another_device" = "Use another device"; "screen_login_subtitle" = "Matrix е отворена мрежа за сигурна, децентрализирана комуникация."; +"screen_notification_settings_mentions_section_title" = "Споменавания"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Повторен опит"; +"screen_recovery_key_confirm_title" = "Потвърдете ключа си за възстановяване"; "screen_report_content_block_user" = "Блокиране на потребителя"; +"screen_reset_encryption_password_placeholder" = "Въведете…"; "screen_room_attachment_source_camera_photo" = "Снимка"; "screen_room_change_permissions_everyone" = "Everyone"; "screen_room_change_permissions_member_moderation" = "Member moderation"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Admins"; "screen_room_change_role_section_moderators" = "Moderators"; "screen_room_change_role_section_users" = "Членове"; +"screen_room_change_role_unsaved_changes_title" = "Save changes?"; "screen_room_details_invite_people_title" = "Поканване на хора"; "screen_room_details_leave_conversation_title" = "Напускане на разговора"; "screen_room_details_leave_room_title" = "Напускане на стаята"; +"screen_room_details_notification_title" = "Известия"; "screen_room_details_roles_and_permissions" = "Roles and permissions"; "screen_room_details_room_name_label" = "Име на стаята"; "screen_room_details_security_title" = "Защита"; "screen_room_details_topic_title" = "Тема"; "screen_room_error_failed_processing_media" = "Failed processing media to upload, please try again."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Remove and ban member"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Само споменавания и ключови думи"; "screen_roomlist_filter_people" = "Хора"; +"screen_server_confirmation_change_server" = "Промяна на доставчика на акаунт"; "screen_signout_confirmation_dialog_submit" = "Изход"; "screen_signout_confirmation_dialog_title" = "Изход"; +"screen_signout_key_backup_offline_title" = "Your keys are still being backed up"; "screen_signout_preference_item" = "Изход"; +"screen_signout_save_recovery_key_title" = "Have you saved your recovery key?"; +"troubleshoot_notifications_entry_point_title" = "Troubleshoot notifications"; diff --git a/ElementX/Resources/Localizations/cs.lproj/Localizable.strings b/ElementX/Resources/Localizations/cs.lproj/Localizable.strings index eb7bc8edb2..34dbc0ec9f 100644 --- a/ElementX/Resources/Localizations/cs.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/cs.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Pozastavit"; "a11y_pin_field" = "Pole pro PIN"; "a11y_play" = "Přehrát"; -"a11y_poll" = "Hlasování"; "a11y_poll_end" = "Hlasování ukončeno"; "a11y_react_with" = "Reagovat s %1$@"; "a11y_react_with_other_emojis" = "Reagovat s dalšími emoji"; @@ -41,6 +40,7 @@ "action_create" = "Vytvořit"; "action_create_a_room" = "Vytvořit místnost"; "action_deactivate" = "Deaktivovat"; +"action_deactivate_account" = "Deaktivovat účet"; "action_decline" = "Odmítnout"; "action_delete_poll" = "Odstranit hlasování"; "action_disable" = "Zakázat"; @@ -64,6 +64,7 @@ "action_leave" = "Odejít"; "action_leave_conversation" = "Opustit konverzaci"; "action_leave_room" = "Opustit místnost"; +"action_load_more" = "Načíst více"; "action_manage_account" = "Spravovat účet"; "action_manage_devices" = "Spravovat zařízení"; "action_message" = "Zpráva"; @@ -93,6 +94,7 @@ "action_send_message" = "Odeslat zprávu"; "action_share" = "Sdílet"; "action_share_link" = "Sdílet odkaz"; +"action_show" = "Show"; "action_sign_in_again" = "Přihlásit se znovu"; "action_signout" = "Odhlásit se"; "action_signout_anyway" = "Přesto se odhlásit"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "Zobrazit na časové ose"; "action_view_source" = "Zobrazit zdroj"; "action_yes" = "Ano"; -"action.load_more" = "Načíst více"; -"action_deactivate_account" = "Deaktivovat účet"; "banner_migrate_to_native_sliding_sync_action" = "Odhlásit se a upgradovat"; "banner_migrate_to_native_sliding_sync_description" = "Váš server nyní podporuje nový, rychlejší protokol. Chcete-li upgradovat, odhlaste se a znovu se přihlaste. Pokud to uděláte nyní, pomůže vám vyhnout se nucenému odhlášení, když bude starý protokol později odstraněn."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Váš domovský server již nepodporuje starý protokol. Chcete-li pokračovat v používání aplikace, odhlaste se a znovu se přihlaste."; @@ -162,6 +162,7 @@ "common_modern" = "Moderní"; "common_mute" = "Ztlumit"; "common_no_results" = "Žádné výsledky"; +"common_no_room_name" = "Žádný název místnosti"; "common_offline" = "Offline"; "common_optic_id_ios" = "Optic ID"; "common_or" = "nebo"; @@ -170,6 +171,8 @@ "common_permalink" = "Trvalý odkaz"; "common_permission" = "Oprávnění"; "common_please_wait" = "Počkejte prosím..."; +"common_poll_end_confirmation" = "Opravdu chcete ukončit toto hlasování?"; +"common_poll_summary" = "Hlasování: %1$@"; "common_poll_total_votes" = "Celkový počet hlasů: %1$@"; "common_poll_undisclosed_text" = "Výsledky se zobrazí po skončení hlasování"; "common_privacy_policy" = "Zásady ochrany osobních údajů"; @@ -200,6 +203,7 @@ "common_settings" = "Nastavení"; "common_shared_location" = "Sdílená poloha"; "common_signing_out" = "Odhlašování"; +"common_something_went_wrong" = "Něco se pokazilo"; "common_starting_chat" = "Zahajování chatu…"; "common_sticker" = "Nálepka"; "common_success" = "Úspěch"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "O čem je tato místnost?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Nelze dešifrovat"; +"common_unable_to_decrypt_no_access" = "Nemáte přístup k této zprávě"; "common_unable_to_invite_message" = "Pozvánky nebylo možné odeslat jednomu nebo více uživatelům."; "common_unable_to_invite_title" = "Nelze odeslat pozvánky"; "common_unlock" = "Odemknout"; @@ -221,24 +226,21 @@ "common_username" = "Uživatelské jméno"; "common_verification_cancelled" = "Ověření zrušeno"; "common_verification_complete" = "Ověření dokončeno"; +"common_verify_device" = "Ověřit zařízení"; "common_video" = "Video"; "common_voice_message" = "Hlasová zpráva"; "common_waiting" = "Čekání..."; "common_waiting_for_decryption_key" = "Čekání na dešifrovací klíč"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "Znovu nezobrazovat"; "common.open_source_licenses" = "Licence s otevřeným zdrojovým kódem"; "common.pinned" = "Připnuto"; "common.send_to" = "Odeslat do"; "common.you" = "Vy"; -"common_no_room_name" = "Žádný název místnosti"; -"common_poll_end_confirmation" = "Opravdu chcete ukončit toto hlasování?"; -"common_poll_summary" = "Hlasování: %1$@"; -"common_something_went_wrong" = "Něco se pokazilo"; -"common_unable_to_decrypt_no_access" = "Nemáte přístup k této zprávě"; -"common_verify_device" = "Ověřit zařízení"; "confirm_recovery_key_banner_message" = "Vaše záloha chatu není aktuálně synchronizována. Abyste si zachovali přístup k záloze chatu, musíte potvrdit klíč pro obnovení."; "confirm_recovery_key_banner_title" = "Potvrďte klíč pro obnovení"; "crash_detection_dialog_content" = "%1$@ havaroval při posledním použití. Chcete se s námi podělit o zprávu o selhání?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "Aby mohla aplikace používat fotoaparát, udělte prosím oprávnění v nastavení systému."; "dialog_permission_generic" = "Udělte prosím oprávnění v nastavení systému."; "dialog_permission_location_description_ios" = "Udělte přístup v Nastavení -> Poloha."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "Tichá oznámení"; "notification_incoming_call" = "Příchozí hovor"; "notification_inline_reply_failed" = "** Nepodařilo se odeslat - otevřete prosím místnost"; -"notification_invitation_action_reject" = "Odmítnout"; "notification_invite_body" = "Vás pozval(a) do chatu"; "notification_invite_body_with_sender" = "%1$@ vás pozval(a) do chatu"; "notification_mentioned_you_body" = "Zmínili vás: %1$@"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "Neplatné URL, ujistěte se, že jste uvedli protokol (http/https) a správnou adresu."; "screen_pinned_timeline_empty_state_description" = "Přidržte zprávu a vyberte „%1$@“, kterou chcete zahrnout sem."; "screen_pinned_timeline_empty_state_headline" = "Připněte důležité zprávy, aby je bylo možné snadno najít"; -"screen_pinned_timeline_screen_title_empty" = "Připnuté zprávy"; "screen_reset_encryption_password_error" = "Došlo k neznámé chybě. Zkontrolujte, zda je heslo k účtu správné a zkuste to znovu."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Zrušit ověření a odeslat"; "screen_resolve_send_failure_changed_identity_subtitle" = "Ověření můžete zrušit a přesto odeslat tuto zprávu, nebo můžete prozatím zrušit a zkusit to znovu později po opětovném ověření %1$@."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Zpráva nebyla odeslána, protože ověřená identita uživatele %1$@ se změnila."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Zpráva nebyla odeslána, protože%1$@ neověřil(a) všechna zařízení."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Zpráva nebyla odeslána, protože jste neověřili jedno nebo více zařízení."; -"screen_account_provider_change" = "Změna poskytovatele účtu"; "screen_account_provider_form_hint" = "Adresa domovského serveru"; "screen_account_provider_form_notice" = "Zadejte hledaný výraz nebo adresu domény."; "screen_account_provider_form_subtitle" = "Vyhledejte společnost, komunitu nebo soukromý server."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "Zálohování zajistí, že neztratíte historii zpráv. %1$@."; "screen_chat_backup_key_backup_title" = "Záloha"; "screen_chat_backup_recovery_action_change" = "Změnit klíč pro obnovení"; -"screen_chat_backup_recovery_action_confirm" = "Potvrďte klíč pro obnovení"; "screen_chat_backup_recovery_action_confirm_description" = "Vaše záloha chatu není aktuálně synchronizována."; "screen_chat_backup_recovery_action_setup" = "Nastavení obnovy"; "screen_chat_backup_recovery_action_setup_description" = "Získejte přístup ke svým zašifrovaným zprávám, pokud ztratíte všechna zařízení nebo jste všude odhlášeni z %1$@."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Zobrazit výsledky až po skončení hlasování"; "screen_create_poll_anonymous_headline" = "Anonymní hlasování"; "screen_create_poll_answer_hint" = "Volba %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Vaše změny nebudou uloženy"; "screen_create_poll_cancel_confirmation_title_ios" = "Zrušit hlasování"; "screen_create_poll_question_desc" = "Otázka nebo téma"; "screen_create_poll_question_hint" = "Čeho se hlasování týká?"; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Skupinové chaty"; "screen_notification_settings_invite_for_me_label" = "Pozvánky"; "screen_notification_settings_mentions_only_disclaimer" = "Váš domovský server tuto možnost v zašifrovaných místnostech nepodporuje, v některých místnostech nemusíte být upozorněni."; -"screen_notification_settings_mentions_section_title" = "Zmínky"; "screen_notification_settings_mode_all" = "Vše"; "screen_notification_settings_mode_mentions" = "Zmínky"; "screen_notification_settings_notification_section_title" = "Upozornit mě na"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Zadejte..."; "screen_recovery_key_confirm_lost_recovery_key" = "Ztratili jste klíč pro obnovení?"; "screen_recovery_key_confirm_success" = "Klíč pro obnovení potvrzen"; -"screen_recovery_key_confirm_title" = "Zadejte klíč pro obnovení"; "screen_recovery_key_copied_to_clipboard" = "Klíč pro obnovení zkopírován"; "screen_recovery_key_generating_key" = "Generování..."; "screen_recovery_key_save_action" = "Uložit klíč pro obnovení"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Ano, resetovat nyní"; "screen_reset_encryption_confirmation_alert_subtitle" = "Tento proces je nevratný."; "screen_reset_encryption_confirmation_alert_title" = "Opravdu chcete obnovit šifrování?"; -"screen_reset_encryption_password_placeholder" = "Zadat..."; "screen_reset_encryption_password_subtitle" = "Potvrďte, že chcete obnovit šifrování."; "screen_reset_encryption_password_title" = "Pro pokračování zadejte heslo k účtu"; "screen_reset_identity_confirmation_subtitle" = "Chystáte se přejít na svůj %1$@ účet a obnovit svou identitu. Poté budete přesměrováni zpět do aplikace."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Správci mají automaticky oprávnění moderátora"; "screen_room_change_role_moderators_title" = "Upravit moderátory"; "screen_room_change_role_unsaved_changes_description" = "Máte neuložené změny."; -"screen_room_change_role_unsaved_changes_title" = "Uložit změny?"; "screen_room_details_add_topic_title" = "Přidat téma"; "screen_room_details_already_a_member" = "Již členem"; "screen_room_details_already_invited" = "Již pozván(a)"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Nepodařilo se zrušit ztišení této místnosti, zkuste to prosím znovu."; "screen_room_details_notification_mode_custom" = "Vlastní"; "screen_room_details_notification_mode_default" = "Výchozí"; -"screen_room_details_notification_title" = "Oznámení"; "screen_room_details_share_room_title" = "Sdílet místnost"; "screen_room_details_title" = "Informace o místnosti"; "screen_room_details_updating_room" = "Aktualizace místnosti..."; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Vykazování %1$@"; "screen_room_member_list_manage_member_ban" = "Odebrat a vykázat člena"; "screen_room_member_list_manage_member_remove" = "Odebrat z místnosti"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Odebrat a vykázat člena"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Pouze odebrat člena"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Odebrat člena a zakázat mu připojení v budoucnu?"; "screen_room_member_list_manage_member_unban_action" = "Zrušit vykázání"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Označit jako přečtené"; "screen_roomlist_mark_as_unread" = "Označit jako nepřečtené"; "screen_roomlist_room_directory_button_title" = "Procházet všechny místnosti"; -"screen_server_confirmation_change_server" = "Změnit poskytovatele účtu"; "screen_server_confirmation_message_login_element_dot_io" = "Soukromý server pro zaměstnance Elementu."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix je otevřená síť pro bezpečnou a decentralizovanou komunikaci."; "screen_server_confirmation_message_register" = "Zde budou uloženy vaše konverzace - podobně jako u poskytovatele e-mailových služeb uchováváte své e-maily."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Chystáte se odhlásit z poslední relace. Pokud se nyní odhlásíte, ztratíte přístup ke svým šifrovaným zprávám."; "screen_signout_key_backup_disabled_title" = "Vypnuli jste zálohování"; "screen_signout_key_backup_offline_subtitle" = "Když jste přešli do režimu offline, vaše klíče se ještě stále zálohovaly. Znovu se připojte, aby bylo možné před odhlášením zálohovat vaše klíče."; -"screen_signout_key_backup_offline_title" = "Vaše klíče jsou stále zálohovány"; "screen_signout_key_backup_ongoing_subtitle" = "Před odhlášením prosím počkejte na dokončení."; "screen_signout_key_backup_ongoing_title" = "Vaše klíče jsou stále zálohovány"; "screen_signout_recovery_disabled_subtitle" = "Chystáte se odhlásit z poslední relace. Pokud se nyní odhlásíte, ztratíte přístup ke svým šifrovaným zprávám."; "screen_signout_recovery_disabled_title" = "Obnovení není nastaveno"; "screen_signout_save_recovery_key_subtitle" = "Chystáte se odhlásit z poslední relace. Pokud se nyní odhlásíte, můžete ztratit přístup k šifrovaným zprávám."; -"screen_signout_save_recovery_key_title" = "Uložili jste si klíč pro obnovení?"; "screen_start_chat_error_starting_chat" = "Při pokusu o zahájení chatu došlo k chybě"; "screen_view_location_title" = "Poloha"; "screen_welcome_bullet_1" = "Hovory, hlasování, vyhledávání a další budou přidány koncem tohoto roku."; @@ -920,7 +908,6 @@ "test_language_identifier" = "en"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Odstraňování problémů"; -"troubleshoot_notifications_entry_point_title" = "Odstraňování problémů s upozorněními"; "troubleshoot_notifications_screen_action" = "Spustit testy"; "troubleshoot_notifications_screen_action_again" = "Spustit testy znovu"; "troubleshoot_notifications_screen_failure" = "Některé testy selhaly. Zkontrolujte prosím podrobnosti."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Ujistěte se, že jsou k dispozici distributoři UnifiedPush."; "troubleshoot_notifications_test_unified_push_failure" = "Nebyli nalezeni žádní push distributoři."; "troubleshoot_notifications_test_unified_push_title" = "Zkontrolovat UnifiedPush"; +"a11y_poll" = "Hlasování"; "dialog_title_error" = "Chyba"; "dialog_title_success" = "Úspěch"; "notification_fallback_content" = "Oznámení"; "notification_invitation_action_join" = "Vstoupit"; +"notification_invitation_action_reject" = "Odmítnout"; "notification_room_action_mark_as_read" = "Označit jako přečtené"; "notification_room_action_quick_reply" = "Rychlá odpověď"; +"screen_pinned_timeline_screen_title_empty" = "Připnuté zprávy"; "screen_room_mentions_at_room_title" = "Všichni"; +"screen_account_provider_change" = "Změnit poskytovatele účtu"; "screen_account_provider_signin_subtitle" = "Zde budou uloženy vaše konverzace - podobně jako u poskytovatele e-mailových služeb uchováváte své e-maily."; "screen_account_provider_signup_subtitle" = "Zde budou uloženy vaše konverzace - podobně jako u poskytovatele e-mailových služeb uchováváte své e-maily."; "screen_analytics_settings_help_us_improve" = "Sdílejte anonymní údaje o používání, které nám pomohou identifikovat problémy."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Znovu uvidíte všechny zprávy od nich."; "screen_blocked_users_unblock_alert_title" = "Odblokovat uživatele"; "screen_bug_report_rash_logs_alert_title" = "%1$@ havaroval při posledním použití. Chcete se s námi podělit o zprávu o selhání?"; +"screen_chat_backup_recovery_action_confirm" = "Zadejte klíč pro obnovení"; +"screen_create_poll_cancel_confirmation_content_ios" = "Vaše změny nebudou uloženy"; "screen_create_room_add_people_title" = "Pozvat přátele"; "screen_create_room_room_name_label" = "Název místnosti"; "screen_create_room_title" = "Vytvořit místnost"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Upravit hlasování"; "screen_identity_use_another_device" = "Použít jiné zařízení"; "screen_login_subtitle" = "Matrix je otevřená síť pro bezpečnou a decentralizovanou komunikaci."; +"screen_notification_settings_mentions_section_title" = "Zmínky"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Zkusit znovu"; +"screen_recovery_key_confirm_title" = "Potvrďte klíč pro obnovení"; "screen_report_content_block_user" = "Zablokovat uživatele"; +"screen_reset_encryption_password_placeholder" = "Zadejte..."; "screen_room_attachment_source_camera_photo" = "Vyfotit"; "screen_room_change_permissions_everyone" = "Všichni"; "screen_room_change_permissions_member_moderation" = "Moderování členů"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Správci"; "screen_room_change_role_section_moderators" = "Moderátoři"; "screen_room_change_role_section_users" = "Členové"; +"screen_room_change_role_unsaved_changes_title" = "Uložit změny?"; "screen_room_details_invite_people_title" = "Pozvat přátele"; "screen_room_details_leave_conversation_title" = "Opustit konverzaci"; "screen_room_details_leave_room_title" = "Opustit místnost"; +"screen_room_details_notification_title" = "Oznámení"; "screen_room_details_roles_and_permissions" = "Role a oprávnění"; "screen_room_details_room_name_label" = "Název místnosti"; "screen_room_details_security_title" = "Zabezpečení"; "screen_room_details_topic_title" = "Téma"; "screen_room_error_failed_processing_media" = "Nahrání média se nezdařilo, zkuste to prosím znovu."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Odebrat a vykázat člena"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Pouze zmínky a klíčová slova"; "screen_roomlist_filter_people" = "Lidé"; +"screen_server_confirmation_change_server" = "Změnit poskytovatele účtu"; "screen_signout_confirmation_dialog_submit" = "Odhlásit se"; "screen_signout_confirmation_dialog_title" = "Odhlásit se"; +"screen_signout_key_backup_offline_title" = "Vaše klíče jsou stále zálohovány"; "screen_signout_preference_item" = "Odhlásit se"; +"screen_signout_save_recovery_key_title" = "Uložili jste si klíč pro obnovení?"; +"troubleshoot_notifications_entry_point_title" = "Odstraňování problémů s upozorněními"; diff --git a/ElementX/Resources/Localizations/de.lproj/Localizable.strings b/ElementX/Resources/Localizations/de.lproj/Localizable.strings index 0716de2081..ad02436387 100644 --- a/ElementX/Resources/Localizations/de.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/de.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Pausieren"; "a11y_pin_field" = "PIN-Feld"; "a11y_play" = "Abspielen"; -"a11y_poll" = "Umfrage"; "a11y_poll_end" = "Umfrage beendet"; "a11y_react_with" = "Reagiere mit %1$@"; "a11y_react_with_other_emojis" = "Mit anderen Emojis reagieren"; @@ -41,6 +40,7 @@ "action_create" = "Erstellen"; "action_create_a_room" = "Raum erstellen"; "action_deactivate" = "Deactivate"; +"action_deactivate_account" = "Deactivate account"; "action_decline" = "Ablehnen"; "action_delete_poll" = "Umfrage löschen"; "action_disable" = "Deaktivieren"; @@ -64,6 +64,7 @@ "action_leave" = "Verlassen"; "action_leave_conversation" = "Unterhaltung verlassen"; "action_leave_room" = "Verlassen"; +"action_load_more" = "Mehr laden ..."; "action_manage_account" = "Konto verwalten"; "action_manage_devices" = "Geräte verwalten"; "action_message" = "Nachricht"; @@ -84,7 +85,7 @@ "action_report_bug" = "Fehler melden"; "action_report_content" = "Inhalt melden"; "action_reset" = "Zurücksetzen"; -"action_reset_identity" = "Reset identity"; +"action_reset_identity" = "Identität zurücksetzen"; "action_retry" = "Erneut versuchen"; "action_retry_decryption" = "Entschlüsselung wiederholen"; "action_save" = "Speichern"; @@ -93,6 +94,7 @@ "action_send_message" = "Nachricht senden"; "action_share" = "Teilen"; "action_share_link" = "Link teilen"; +"action_show" = "Show"; "action_sign_in_again" = "Erneut anmelden"; "action_signout" = "Abmelden"; "action_signout_anyway" = "Trotzdem abmelden"; @@ -108,13 +110,11 @@ "action_view_in_timeline" = "Im Chatverlauf anzeigen"; "action_view_source" = "Quellcode anzeigen"; "action_yes" = "Ja"; -"action.load_more" = "Mehr laden ..."; -"action_deactivate_account" = "Deactivate account"; "banner_migrate_to_native_sliding_sync_action" = "Abmelden und aktualisieren"; "banner_migrate_to_native_sliding_sync_description" = "Dein Server unterstützt jetzt ein neues, schnelleres Protokoll. Melde dich ab und melde dich wieder an, um zu aktualisieren. Wenn du das jetzt tust, vermeidest du eine erzwungene Abmeldung, wenn das alte Protokoll später entfernt wird."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Dein Homeserver unterstützt das alte Protokoll nicht mehr. Bitte logge dich aus und melde dich wieder an, um die App weiter zu nutzen."; "banner_migrate_to_native_sliding_sync_title" = "Aktualisierung verfügbar"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; +"banner.set_up_recovery.content" = "Erstelle einen neuen Wiederherstellungsschlüssel, mit dem du deinen verschlüsselten Nachrichtenverlauf wiederherstellen kannst, wenn du dich an einem neuen Gerät anmeldest."; "banner.set_up_recovery.title" = "Wiederherstellung einrichten"; "common_about" = "Über"; "common_acceptable_use_policy" = "Nutzungsrichtlinie"; @@ -162,6 +162,7 @@ "common_modern" = "Modern"; "common_mute" = "Stummschalten"; "common_no_results" = "Keine Ergebnisse"; +"common_no_room_name" = "Kein Raumname"; "common_offline" = "Offline"; "common_optic_id_ios" = "Optic ID"; "common_or" = "oder"; @@ -170,6 +171,8 @@ "common_permalink" = "Permalink"; "common_permission" = "Erlaubnis"; "common_please_wait" = "Bitte warten ..."; +"common_poll_end_confirmation" = "Bist du sicher, dass du diese Umfrage beenden möchtest?"; +"common_poll_summary" = "Umfrage: %1$@"; "common_poll_total_votes" = "Stimmen insgesamt: %1$@"; "common_poll_undisclosed_text" = "Die Ergebnisse werden nach Ende der Umfrage angezeigt"; "common_privacy_policy" = "Datenschutz­erklärung"; @@ -200,6 +203,7 @@ "common_settings" = "Einstellungen"; "common_shared_location" = "Geteilter Standort"; "common_signing_out" = "Abmelden"; +"common_something_went_wrong" = "Es ist ein Fehler aufgetreten."; "common_starting_chat" = "Chat wird gestartet..."; "common_sticker" = "Sticker"; "common_success" = "Erfolg"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "Worum geht es in diesem Raum?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Entschlüsselung nicht möglich"; +"common_unable_to_decrypt_no_access" = "Du hast kein Recht diese Nachricht zu lesen."; "common_unable_to_invite_message" = "Einladungen konnten nicht an einen oder mehrere Benutzer gesendet werden."; "common_unable_to_invite_title" = "Einladung(en) konnte(n) nicht gesendet werden"; "common_unlock" = "Entsperren"; @@ -221,24 +226,21 @@ "common_username" = "Benutzername"; "common_verification_cancelled" = "Verifizierung abgebrochen"; "common_verification_complete" = "Verifizierung abgeschlossen"; +"common_verify_device" = "Gerät verifizieren"; "common_video" = "Video"; "common_voice_message" = "Sprachnachricht"; "common_waiting" = "Warten…"; "common_waiting_for_decryption_key" = "Warte auf diese Nachricht"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "Nicht mehr anzeigen"; "common.open_source_licenses" = "Open-Source-Lizenzen"; "common.pinned" = "Fixiert"; "common.send_to" = "Senden an"; "common.you" = "You"; -"common_no_room_name" = "Kein Raumname"; -"common_poll_end_confirmation" = "Bist du sicher, dass du diese Umfrage beenden möchtest?"; -"common_poll_summary" = "Umfrage: %1$@"; -"common_something_went_wrong" = "Es ist ein Fehler aufgetreten."; -"common_unable_to_decrypt_no_access" = "Du hast kein Recht diese Nachricht zu lesen."; -"common_verify_device" = "Gerät verifizieren"; "confirm_recovery_key_banner_message" = "Dein Chat-Backup ist derzeit nicht synchronisiert. Du musst deinen Wiederherstellungsschlüssel bestätigen, um Zugriff auf dein Chat-Backup zu erhalten."; "confirm_recovery_key_banner_title" = "Wiederherstellungsschlüssel bestätigen."; "crash_detection_dialog_content" = "%1$@ ist bei der letzten Nutzung abgestürzt. Möchtest du einen Absturzbericht mit uns teilen?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "Damit die Anwendung die Kamera verwenden kann, erteile bitte die Erlaubnis in den Systemeinstellungen."; "dialog_permission_generic" = "Bitte erteile die Erlaubnis in den Systemeinstellungen."; "dialog_permission_location_description_ios" = "Erlaube den Zugriff unter Einstellungen -> Standort."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "Stumme Benachrichtigungen"; "notification_incoming_call" = "Eingehender Anruf"; "notification_inline_reply_failed" = "** Fehler beim Senden - bitte Raum öffnen"; -"notification_invitation_action_reject" = "Ablehnen"; "notification_invite_body" = "Du wurdest zu einem Chat eingeladen"; "notification_invite_body_with_sender" = "%1$@ invited you to chat"; "notification_mentioned_you_body" = "Hat Dich erwähnt: %1$@"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "Ungültige URL, bitte stelle sicher, dass du das Protokoll (http/https) und die richtige Adresse angibst."; "screen_pinned_timeline_empty_state_description" = "Drücke auf eine Nachricht und wähle “%1$@”, um sie hier einzufügen."; "screen_pinned_timeline_empty_state_headline" = "Fixiere wichtige Nachrichten, so dass sie leicht gefunden werden können"; -"screen_pinned_timeline_screen_title_empty" = "Fixierte Nachrichten"; "screen_reset_encryption_password_error" = "Es ist ein unbekannter Fehler aufgetreten. Bitte überprüfe das Passwort deines Kontos und versuche es erneut."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Verifizierung zurückziehen und senden"; "screen_resolve_send_failure_changed_identity_subtitle" = "Du kannst deine Verifizierung zurückziehen und diese Nachricht trotzdem senden, oder du kannst sie vorerst abbrechen und es später noch einmal versuchen, nachdem du %1$@ erneut verifiziert hast."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Nachricht nicht gesendet, weil sich die verifizierte Identität von %1$@ geändert hat."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Nachricht wurde nicht gesendet, weil %1$@ nicht alle Geräte verifiziert hat"; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "Kontoanbieter ändern"; "screen_account_provider_form_hint" = "Homeserver-Adresse"; "screen_account_provider_form_notice" = "Gib einen Suchbegriff oder eine Domainadresse ein."; "screen_account_provider_form_subtitle" = "Suche nach einem Unternehmen, einer Community oder einem privaten Server."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "Das Backup stellt sicher, dass du deinen Nachrichtenverlauf nicht verlierst. %1$@."; "screen_chat_backup_key_backup_title" = "Backup"; "screen_chat_backup_recovery_action_change" = "Wiederherstellungsschlüssel ändern"; -"screen_chat_backup_recovery_action_confirm" = "Wiederherstellungsschlüssel bestätigen"; "screen_chat_backup_recovery_action_confirm_description" = "Dein Chat-Backup ist derzeit nicht synchronisiert."; "screen_chat_backup_recovery_action_setup" = "Wiederherstellung einrichten"; "screen_chat_backup_recovery_action_setup_description" = "Erhalte Zugriff auf deine verschlüsselten Nachrichten, wenn du alle deine Geräte verlierst oder von %1$@ überall abgemeldet bist."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Ergebnisse erst nach Ende der Umfrage anzeigen"; "screen_create_poll_anonymous_headline" = "Anonyme Umfrage"; "screen_create_poll_answer_hint" = "Option %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Deine Änderungen werden nicht gespeichert"; "screen_create_poll_cancel_confirmation_title_ios" = "Umfrage abbrechen"; "screen_create_poll_question_desc" = "Frage oder Thema"; "screen_create_poll_question_hint" = "Worum geht es bei der Umfrage?"; @@ -479,9 +476,9 @@ "screen_edit_profile_title" = "Profil bearbeiten"; "screen_edit_profile_updating_details" = "Profil wird aktualisiert..."; "screen_encryption_reset_action_continue_reset" = "Zurücksetzen fortsetzen"; -"screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history"; -"screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; +"screen_encryption_reset_bullet_1" = "Deine Kontodaten, Kontakte, Einstellungen und die Liste der Chats bleiben erhalten"; +"screen_encryption_reset_bullet_2" = "Du verlierst alle deine bisherigen Nachrichten sofern sie nicht auf einem anderen Gerät vorliegen"; +"screen_encryption_reset_bullet_3" = "Du musst alle deine bestehenden Geräte und Kontakte erneut verifizieren."; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; "screen_identity_confirmation_cannot_confirm" = "Can't confirm?"; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Gruppenchats"; "screen_notification_settings_invite_for_me_label" = "Einladungen"; "screen_notification_settings_mentions_only_disclaimer" = "Dein Homeserver unterstützt diese Option in verschlüsselten Chat nicht. In einigen Chats wirst du möglicherweise nicht benachrichtigt."; -"screen_notification_settings_mentions_section_title" = "Erwähnungen"; "screen_notification_settings_mode_all" = "Alle"; "screen_notification_settings_mode_mentions" = "Erwähnungen"; "screen_notification_settings_notification_section_title" = "Benachrichtige mich bei"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Eingeben..."; "screen_recovery_key_confirm_lost_recovery_key" = "Hast du deinen Wiederherstellungschlüssel vergessen?"; "screen_recovery_key_confirm_success" = "Wiederherstellungsschlüssel bestätigt"; -"screen_recovery_key_confirm_title" = "Bitte Wiederherstellungsschlüssel eingeben"; "screen_recovery_key_copied_to_clipboard" = "Wiederherstellungsschlüssel kopiert"; "screen_recovery_key_generating_key" = "Generieren…"; "screen_recovery_key_save_action" = "Wiederherstellungsschlüssel speichern"; @@ -634,12 +629,11 @@ "screen_report_content_block_user_hint" = "Prüfe, ob du alle aktuellen und zukünftigen Nachrichten dieses Benutzers ausblenden möchtest"; "screen_report_content_explanation" = "Diese Meldung wird an den Administrator deines Homeservers weitergeleitet. Dieser kann keine verschlüsselten Nachrichten lesen."; "screen_report_content_hint" = "Grund für die Meldung dieses Inhalts"; -"screen_reset_encryption_confirmation_alert_action" = "Yes, reset now"; -"screen_reset_encryption_confirmation_alert_subtitle" = "This process is irreversible."; -"screen_reset_encryption_confirmation_alert_title" = "Are you sure you want to reset your identity?"; -"screen_reset_encryption_password_placeholder" = "Eingeben..."; -"screen_reset_encryption_password_subtitle" = "Confirm that you want to reset your identity."; -"screen_reset_encryption_password_title" = "Enter your account password to continue"; +"screen_reset_encryption_confirmation_alert_action" = "Ja, zurücksetzen"; +"screen_reset_encryption_confirmation_alert_subtitle" = "Das Zurücksetzen kann nicht rückgängig gemacht werden."; +"screen_reset_encryption_confirmation_alert_title" = "Bist du sicher, dass du deine Identität zurücksetzen möchtest?"; +"screen_reset_encryption_password_subtitle" = "Bestätige, dass du deine Identität zurücksetzen möchtest."; +"screen_reset_encryption_password_title" = "Gib dein Passwort ein, um fortzufahren"; "screen_reset_identity_confirmation_subtitle" = "Du wirst jetzt zu deinem %1$@ Konto geleitet, um deine Identität zurückzusetzen. Danach wirst du zur App zurückgebracht."; "screen_reset_identity_confirmation_title" = "Kannst du das nicht bestätigen? Gehe zu deinem Konto, um deine Identität zurückzusetzen."; "screen_room_alias_resolver_resolve_alias_failure" = "Der Raum-Alias konnte nicht ermittelt werden."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Administratoren haben automatisch Moderatorenrechte"; "screen_room_change_role_moderators_title" = "Moderatoren bearbeiten"; "screen_room_change_role_unsaved_changes_description" = "Du hast nicht gespeicherte Änderungen."; -"screen_room_change_role_unsaved_changes_title" = "Änderungen speichern?"; "screen_room_details_add_topic_title" = "Thema hinzufügen"; "screen_room_details_already_a_member" = "Bereits Mitglied"; "screen_room_details_already_invited" = "Bereits eingeladen"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Die Deaktivierung der Stummschaltung ist fehlgeschlagen, bitte versuche es erneut."; "screen_room_details_notification_mode_custom" = "Benutzerdefiniert"; "screen_room_details_notification_mode_default" = "Standard"; -"screen_room_details_notification_title" = "Benachrichtigungen"; "screen_room_details_share_room_title" = "Teilen"; "screen_room_details_title" = "Informationen"; "screen_room_details_updating_room" = "Raum wird aktualisiert…"; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "%1$@ wird gesperrt."; "screen_room_member_list_manage_member_ban" = "Mitglied entfernen und sperren"; "screen_room_member_list_manage_member_remove" = "Mitglied entfernen"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Mitglied entfernen und sperren"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Mitglied nur entfernen"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Mitglied entfernen und den erneuten Beitritt sperren?"; "screen_room_member_list_manage_member_unban_action" = "Sperre aufheben"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Als gelesen markieren"; "screen_roomlist_mark_as_unread" = "Als ungelesen markieren"; "screen_roomlist_room_directory_button_title" = "Alle Räume durchsuchen"; -"screen_server_confirmation_change_server" = "Kontoanbieter wechseln"; "screen_server_confirmation_message_login_element_dot_io" = "Ein privater Server für die Mitarbeiter von Element."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix ist ein offenes Netzwerk für eine sichere, dezentrale Kommunikation."; "screen_server_confirmation_message_register" = "Hier werden deine Gespräche gespeichert - so wie du deine E-Mails bei einem E-Mail-Anbieter aufbewahren würden."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Du bist dabei, dich von deiner letzten Sitzung abzumelden. Wenn du dich jetzt abmeldest, verlierst du den Zugriff auf deine verschlüsselten Nachrichten."; "screen_signout_key_backup_disabled_title" = "Du hast das Backup deaktiviert."; "screen_signout_key_backup_offline_subtitle" = "Deine Schlüssel wurden noch gesichert, als du offline gegangen bist. Stelle die Verbindung wieder her, damit deine Schlüssel gesichert werden können, bevor du dich abmeldest."; -"screen_signout_key_backup_offline_title" = "Deine Schlüssel werden noch gesichert"; "screen_signout_key_backup_ongoing_subtitle" = "Bitte warte, bis der Vorgang abgeschlossen ist, bevor du dich abmeldest."; "screen_signout_key_backup_ongoing_title" = "Deine Schlüssel werden noch gesichert"; "screen_signout_recovery_disabled_subtitle" = "Du bist dabei, dich von deiner letzten Sitzung abzumelden. Wenn du dich jetzt abmeldest, verlierst du den Zugriff auf deine verschlüsselten Nachrichten."; "screen_signout_recovery_disabled_title" = "Wiederherstellung nicht eingerichtet"; "screen_signout_save_recovery_key_subtitle" = "Du bist dabei, dich von deiner letzten Sitzung abzumelden. Wenn du dich jetzt abmeldest, verlierst du möglicherweise den Zugriff auf deine verschlüsselten Nachrichten."; -"screen_signout_save_recovery_key_title" = "Hast du deinen Wiederherstellungsschlüssel gespeichert?"; "screen_start_chat_error_starting_chat" = "Beim Versuch, einen Chat zu starten, ist ein Fehler aufgetreten"; "screen_view_location_title" = "Standort"; "screen_welcome_bullet_1" = "Anrufe, Umfragen, Suchfunktionen und mehr werden im Laufe des Jahres hinzugefügt."; @@ -920,7 +908,6 @@ "test_language_identifier" = "en"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Fehlerbehebung"; -"troubleshoot_notifications_entry_point_title" = "Fehlerbehebung für Benachrichtigungen"; "troubleshoot_notifications_screen_action" = "Tests durchführen"; "troubleshoot_notifications_screen_action_again" = "Tests erneut durchführen"; "troubleshoot_notifications_screen_failure" = "Einige Tests sind fehlgeschlagen. Bitte überprüfe die Details."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Stelle sicher, dass UnifiedPush-Verteiler verfügbar sind."; "troubleshoot_notifications_test_unified_push_failure" = "Keine Push-Verteiler gefunden."; "troubleshoot_notifications_test_unified_push_title" = "UnifiedPush prüfen"; +"a11y_poll" = "Umfrage"; "dialog_title_error" = "Fehler"; "dialog_title_success" = "Erfolg"; "notification_fallback_content" = "Mitteilung"; "notification_invitation_action_join" = "Beitreten"; +"notification_invitation_action_reject" = "Ablehnen"; "notification_room_action_mark_as_read" = "Als gelesen markieren"; "notification_room_action_quick_reply" = "Schnelle Antwort"; +"screen_pinned_timeline_screen_title_empty" = "Fixierte Nachrichten"; "screen_room_mentions_at_room_title" = "Alle"; +"screen_account_provider_change" = "Kontoanbieter wechseln"; "screen_account_provider_signin_subtitle" = "Hier werden deine Gespräche gespeichert - so wie du deine E-Mails bei einem E-Mail-Anbieter aufbewahren würden."; "screen_account_provider_signup_subtitle" = "Hier werden deine Gespräche gespeichert - so wie du deine E-Mails bei einem E-Mail-Anbieter aufbewahren würden."; "screen_analytics_settings_help_us_improve" = "Teile anonyme Nutzungsdaten, um uns bei der Identifizierung von Problemen zu helfen."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Der Nutzer kann dir wieder Nachrichten senden & alle Nachrichten des Nutzers werden wieder angezeigt."; "screen_blocked_users_unblock_alert_title" = "Blockierung aufheben"; "screen_bug_report_rash_logs_alert_title" = "%1$@ ist bei der letzten Nutzung abgestürzt. Möchtest du einen Absturzbericht mit uns teilen?"; +"screen_chat_backup_recovery_action_confirm" = "Wiederherstellungsschlüssel eingeben"; +"screen_create_poll_cancel_confirmation_content_ios" = "Deine Änderungen werden nicht gespeichert"; "screen_create_room_add_people_title" = "Personen einladen"; "screen_create_room_room_name_label" = "Raumname"; "screen_create_room_title" = "Raum erstellen"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Umfrage bearbeiten"; "screen_identity_use_another_device" = "Ein anderes Gerät verwenden"; "screen_login_subtitle" = "Matrix ist ein offenes Netzwerk für eine sichere, dezentrale Kommunikation."; +"screen_notification_settings_mentions_section_title" = "Erwähnungen"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Erneut versuchen"; +"screen_recovery_key_confirm_title" = "Wiederherstellungsschlüssel bestätigen."; "screen_report_content_block_user" = "Benutzer blockieren"; +"screen_reset_encryption_password_placeholder" = "Eingeben..."; "screen_room_attachment_source_camera_photo" = "Foto aufnehmen"; "screen_room_change_permissions_everyone" = "Alle"; "screen_room_change_permissions_member_moderation" = "Moderation der Mitglieder"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Administratoren"; "screen_room_change_role_section_moderators" = "Moderatoren"; "screen_room_change_role_section_users" = "Mitglieder"; +"screen_room_change_role_unsaved_changes_title" = "Änderungen speichern?"; "screen_room_details_invite_people_title" = "Personen einladen"; "screen_room_details_leave_conversation_title" = "Unterhaltung verlassen"; "screen_room_details_leave_room_title" = "Verlassen"; +"screen_room_details_notification_title" = "Benachrichtigungen"; "screen_room_details_roles_and_permissions" = "Rollen und Berechtigungen"; "screen_room_details_room_name_label" = "Raumname"; "screen_room_details_security_title" = "Sicherheit"; "screen_room_details_topic_title" = "Thema"; "screen_room_error_failed_processing_media" = "Fehler beim Verarbeiten des hochgeladenen Mediums. Bitte versuche es erneut."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Mitglied entfernen und sperren"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Nur Erwähnungen und Schlüsselwörter"; "screen_roomlist_filter_people" = "Personen"; +"screen_server_confirmation_change_server" = "Kontoanbieter wechseln"; "screen_signout_confirmation_dialog_submit" = "Abmelden"; "screen_signout_confirmation_dialog_title" = "Abmelden"; +"screen_signout_key_backup_offline_title" = "Deine Schlüssel werden noch gesichert"; "screen_signout_preference_item" = "Abmelden"; +"screen_signout_save_recovery_key_title" = "Hast du deinen Wiederherstellungsschlüssel gespeichert?"; +"troubleshoot_notifications_entry_point_title" = "Fehlerbehebung für Benachrichtigungen"; diff --git a/ElementX/Resources/Localizations/el.lproj/Localizable.strings b/ElementX/Resources/Localizations/el.lproj/Localizable.strings index 3e5d0e3151..8c7fdf9943 100644 --- a/ElementX/Resources/Localizations/el.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/el.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Παύση"; "a11y_pin_field" = "Πεδίο PIN"; "a11y_play" = "Αναπαραγωγή"; -"a11y_poll" = "Δημοσκόπηση"; "a11y_poll_end" = "Ολοκληρωμένη δημοσκόπηση"; "a11y_react_with" = "Αντέδρασε με %1$@"; "a11y_react_with_other_emojis" = "Αντέδρασε με άλλα emoji"; @@ -41,6 +40,7 @@ "action_create" = "Δημιουργία"; "action_create_a_room" = "Δημιούργησε ένα δωμάτιο"; "action_deactivate" = "Απενεργοποίηση"; +"action_deactivate_account" = "Απενεργοποίηση λογαριασμού"; "action_decline" = "Απόρριψη"; "action_delete_poll" = "Διαγραφή Δημοσκόπησης"; "action_disable" = "Απενεργοποίηση"; @@ -64,6 +64,7 @@ "action_leave" = "Αποχώρηση"; "action_leave_conversation" = "Αποχώρηση από τη συζήτηση"; "action_leave_room" = "Αποχώρηση από το δωμάτιο"; +"action_load_more" = "Φόρτωσε περισσότερα"; "action_manage_account" = "Διαχείριση λογαριασμού"; "action_manage_devices" = "Διαχείριση συσκευών"; "action_message" = "Στείλε"; @@ -93,6 +94,7 @@ "action_send_message" = "Αποστολή μηνύματος"; "action_share" = "Κοινή χρήση"; "action_share_link" = "Κοινή χρήση συνδέσμου"; +"action_show" = "Εμφάνιση"; "action_sign_in_again" = "Συνδέσου ξανά"; "action_signout" = "Αποσύνδεση"; "action_signout_anyway" = "Αποσύνδεση ούτως ή άλλως"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "Προβολή στο χρονοδιάγραμμα"; "action_view_source" = "Προβολή πηγής"; "action_yes" = "Ναι"; -"action.load_more" = "Φόρτωσε περισσότερα"; -"action_deactivate_account" = "Απενεργοποίηση λογαριασμού"; "banner_migrate_to_native_sliding_sync_action" = "Αποσύνδεση & Αναβάθμιση"; "banner_migrate_to_native_sliding_sync_description" = "Ο διακομιστής σου υποστηρίζει τώρα ένα νέο, ταχύτερο πρωτόκολλο. Αποσυνδέσου και συνδέσου ξανά για αναβάθμιση τώρα. Κάνοντας αυτό τώρα θα σε βοηθήσει να αποφύγεις μια αναγκαστική αποσύνδεση όταν το παλιό πρωτόκολλο καταργηθεί αργότερα."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Ο οικιακός διακομιστής σου δεν υποστηρίζει πλέον το παλιό πρωτόκολλο. Αποσυνδέσου και συνδέσου ξανά για να συνεχίσεις να χρησιμοποιείς την εφαρμογή."; @@ -162,6 +162,7 @@ "common_modern" = "Σύγχρονη"; "common_mute" = "Σίγαση"; "common_no_results" = "Κανένα αποτέλεσμα"; +"common_no_room_name" = "Χωρίς όνομα δωματίου"; "common_offline" = "Εκτός σύνδεσης"; "common_optic_id_ios" = "Optic ID"; "common_or" = "ή"; @@ -170,6 +171,8 @@ "common_permalink" = "Μόνιμος σύνδεσμος"; "common_permission" = "Αδεια"; "common_please_wait" = "Παρακαλώ περίμενε..."; +"common_poll_end_confirmation" = "Θες σίγουρα να τερματίσεις αυτή τη δημοσκόπηση;"; +"common_poll_summary" = "Δημοσκόπηση: %1$@"; "common_poll_total_votes" = "Σύνολο ψήφων: %1$@"; "common_poll_undisclosed_text" = "Τα αποτελέσματα θα εμφανιστούν μετά τη λήξη της ψηφοφορίας"; "common_privacy_policy" = "Πολιτική απορρήτου"; @@ -200,6 +203,7 @@ "common_settings" = "Ρυθμίσεις"; "common_shared_location" = "Κοινόχρηστη τοποθεσία"; "common_signing_out" = "Αποσύνδεση"; +"common_something_went_wrong" = "Κάτι πήγε στραβά"; "common_starting_chat" = "Έναρξη συνομιλίας..."; "common_sticker" = "Αυτοκόλλητο"; "common_success" = "Επιτυχία"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "Τί αφορά το δωμάτιο;"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Δεν είναι δυνατή η αποκρυπτογράφηση"; +"common_unable_to_decrypt_no_access" = "Δεν έχεις πρόσβαση σε αυτό το μήνυμα"; "common_unable_to_invite_message" = "Δεν ήταν δυνατή η αποστολή προσκλήσεων σε έναν ή περισσότερους χρήστες."; "common_unable_to_invite_title" = "Δεν είναι δυνατή η αποστολή προσκλήσεων"; "common_unlock" = "Ξεκλείδωμα"; @@ -221,24 +226,21 @@ "common_username" = "Όνομα χρήστη"; "common_verification_cancelled" = "Η επαλήθευση ακυρώθηκε"; "common_verification_complete" = "Η επαλήθευση ολοκληρώθηκε"; +"common_verify_device" = "Επαλήθευση συσκευής"; "common_video" = "Βίντεο"; "common_voice_message" = "Φωνητικό μήνυμα"; "common_waiting" = "Αναμονή…"; "common_waiting_for_decryption_key" = "Αναμονή για αυτό το μήνυμα"; +"common.copied_to_clipboard" = "Αντιγράφηκε στο πρόχειρο"; "common.do_not_show_this_again" = "Να μην εμφανιστεί ξανά"; "common.open_source_licenses" = "Άδειες ανοιχτού κώδικα"; "common.pinned" = "Καρφιτσωμένο"; "common.send_to" = "Αποστολή σε"; -"common.you" = "You"; -"common_no_room_name" = "Χωρίς όνομα δωματίου"; -"common_poll_end_confirmation" = "Θες σίγουρα να τερματίσεις αυτή τη δημοσκόπηση;"; -"common_poll_summary" = "Δημοσκόπηση: %1$@"; -"common_something_went_wrong" = "Κάτι πήγε στραβά"; -"common_unable_to_decrypt_no_access" = "Δεν έχεις πρόσβαση σε αυτό το μήνυμα"; -"common_verify_device" = "Επαλήθευση συσκευής"; +"common.you" = "Εσύ"; "confirm_recovery_key_banner_message" = "Το αντίγραφο ασφαλείας της συνομιλίας σου δεν είναι συγχρονισμένο αυτήν τη στιγμή. Πρέπει να εισαγάγεις το κλειδί ανάκτησης για να διατηρήσεις την πρόσβαση στο αντίγραφο ασφαλείας της συνομιλίας σου."; "confirm_recovery_key_banner_title" = "Εισήγαγε το κλειδί ανάκτησης"; "crash_detection_dialog_content" = "Το %1$@ διακόπηκε την τελευταία φορά που χρησιμοποιήθηκε. Θα 'θελες να μοιραστείς μια αναφορά σφάλματος μαζί μας;"; +"crypto_identity_change_pin_violation" = "Η ταυτότητα του χρήστη %1$@ φαίνεται να έχει αλλάξει. %2$@"; "dialog_permission_camera" = "Για να επιτρέψεις στην εφαρμογή να χρησιμοποιεί την κάμερα, παραχώρησε την άδεια στις ρυθμίσεις συστήματος."; "dialog_permission_generic" = "Παρακαλώ παραχώρησε την άδεια στις ρυθμίσεις συστήματος."; "dialog_permission_location_description_ios" = "Παραχώρησε πρόσβαση στις Ρυθμίσεις -> Τοποθεσία."; @@ -291,14 +293,13 @@ "notification_channel_silent" = "Αθόρυβες ειδοποιήσεις"; "notification_incoming_call" = "Εισερχόμενη κλήση"; "notification_inline_reply_failed" = "** Αποτυχία αποστολής - παρακαλώ άνοιξε το δωμάτιο"; -"notification_invitation_action_reject" = "Απόρριψη"; "notification_invite_body" = "Σε προσκάλεσε να συνομιλήσετε"; -"notification_invite_body_with_sender" = "%1$@ invited you to chat"; +"notification_invite_body_with_sender" = "Ο χρήστης %1$@ σε προσκάλεσε σε συνομιλία"; "notification_mentioned_you_body" = "Σέ ανέφερε: %1$@"; "notification_new_messages" = "Νέα Μηνύματα"; "notification_reaction_body" = "Αντέδρασε με %1$@"; "notification_room_invite_body" = "Σέ προσκάλεσε να συμμετάσχεις στο δωμάτιο"; -"notification_room_invite_body_with_sender" = "%1$@ invited you to join the room"; +"notification_room_invite_body_with_sender" = "Ο χρήστης %1$@ σε προσκάλεσε να συμμετάσχεις στο δωμάτιο"; "notification_sender_me" = "Εγώ"; "notification_sender_mention_reply" = "Ο χρήστης %1$@ αναφέρθηκε ή απάντησε"; "notification_test_push_notification_content" = "Βλέπεις την ειδοποίηση! Κάνε μου κλικ!"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "Μη έγκυρη διεύθυνση URL, βεβαιώσου ότι έχεις συμπεριλάβει το πρωτόκολλο (http/https) και τη σωστή διεύθυνση."; "screen_pinned_timeline_empty_state_description" = "Πάτα σε ένα μήνυμα και επέλεξε «%1$@» για να συμπεριληφθεί εδώ."; "screen_pinned_timeline_empty_state_headline" = "Καρφίτσωσε σημαντικά μηνύματα, ώστε να μπορούν να εντοπιστούν εύκολα"; -"screen_pinned_timeline_screen_title_empty" = "Καρφιτσωμένα μηνύματα"; "screen_reset_encryption_password_error" = "Συνέβη ένα άγνωστο σφάλμα. Έλεγξε ότι ο κωδικός πρόσβασης του λογαριασμού σου είναι σωστός και δοκίμασε ξανά."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Ανάκληση επαλήθευσης και αποστολή"; "screen_resolve_send_failure_changed_identity_subtitle" = "Μπορείτε να ανακαλέσεις την επαλήθευσή σου και να στείλεις αυτό το μήνυμα όπως και να 'χει ή μπορείς να το ακυρώσεις προς το παρόν και να προσπαθήσεις ξανά αργότερα μετά την επαλήθευση του χρήστη %1$@."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Το μήνυμα δεν στάλθηκε επειδή η επαληθευμένη ταυτότητα του χρήστη %1$@ έχει αλλάξει."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Το μήνυμα δεν στάλθηκε επειδή ο χρήστης %1$@ δεν έχει επαληθεύσει όλες τις συσκευές."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Το μήνυμα δεν στάλθηκε επειδή δεν έχεις επαληθεύσει τουλάχιστον μία από τις συσκευές σου."; -"screen_account_provider_change" = "Αλλαγή παρόχου λογαριασμού"; "screen_account_provider_form_hint" = "Διεύθυνση οικιακού διακομιστή"; "screen_account_provider_form_notice" = "Εισήγαγε έναν όρο αναζήτησης ή μια διεύθυνση τομέα."; "screen_account_provider_form_subtitle" = "Αναζήτησε μια εταιρεία, κοινότητα ή ιδιωτικό διακομιστή."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "Η δημιουργία αντιγράφων ασφαλείας διασφαλίζει ότι δεν θα χάσεις το ιστορικό μηνυμάτων σου. %1$@."; "screen_chat_backup_key_backup_title" = "Αντίγραφο ασφαλείας"; "screen_chat_backup_recovery_action_change" = "Αλλαγή κλειδιού ανάκτησης"; -"screen_chat_backup_recovery_action_confirm" = "Εισαγωγή κλειδιού ανάκτησης"; "screen_chat_backup_recovery_action_confirm_description" = "Το αντίγραφο ασφαλείας της συνομιλίας σου δεν είναι συγχρονισμένο αυτήν τη στιγμή."; "screen_chat_backup_recovery_action_setup" = "Ρύθμιση ανάκτησης"; "screen_chat_backup_recovery_action_setup_description" = "Απόκτησε πρόσβαση στα κρυπτογραφημένα σου μηνύματα εάν χάσεις όλες τις συσκευές σου ή έχεις αποσυνδεθεί από το %1$@ παντού."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Εμφάνιση αποτελεσμάτων μόνο μετά τη λήξη της ψηφοφορίας"; "screen_create_poll_anonymous_headline" = "Απόκρυψη ψήφων"; "screen_create_poll_answer_hint" = "Επιλογή %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Οι αλλαγές σου δεν θα αποθηκευτούν"; "screen_create_poll_cancel_confirmation_title_ios" = "Ακύρωση Δημοσκόπησης"; "screen_create_poll_question_desc" = "Ερώτηση ή θέμα"; "screen_create_poll_question_hint" = "Τί αφορά η δημοσκόπηση;"; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Ομαδικές συνομιλίες"; "screen_notification_settings_invite_for_me_label" = "Προσκλήσεις"; "screen_notification_settings_mentions_only_disclaimer" = "Ο οικιακός διακομιστής σου δεν υποστηρίζει αυτήν την επιλογή σε κρυπτογραφημένα δωμάτια, ενδέχεται να μην λάβεις ειδοποίηση σε ορισμένα δωμάτια."; -"screen_notification_settings_mentions_section_title" = "Αναφορές"; "screen_notification_settings_mode_all" = "Όλα"; "screen_notification_settings_mode_mentions" = "Αναφορές"; "screen_notification_settings_notification_section_title" = "Ειδοποίησέ με για"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Εισαγωγή..."; "screen_recovery_key_confirm_lost_recovery_key" = "Έχασες το κλειδί ανάκτησης;"; "screen_recovery_key_confirm_success" = "Επιβεβαιώθηκε το κλειδί ανάκτησης"; -"screen_recovery_key_confirm_title" = "Εισήγαγε το κλειδί ανάκτησης"; "screen_recovery_key_copied_to_clipboard" = "Αντιγράφηκε το κλειδί ανάκτησης"; "screen_recovery_key_generating_key" = "Δημιουργία..."; "screen_recovery_key_save_action" = "Αποθήκευση κλειδιού ανάκτησης"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Ναι, επαναφορά τώρα"; "screen_reset_encryption_confirmation_alert_subtitle" = "Η διαδικασία είναι μη αναστρέψιμη."; "screen_reset_encryption_confirmation_alert_title" = "Σίγουρα θες να επαναφέρεις την ταυτότητά σου;"; -"screen_reset_encryption_password_placeholder" = "Εισαγωγή..."; "screen_reset_encryption_password_subtitle" = "Επιβεβαίωσε ότι θες να επαναφέρεις την ταυτότητά σου."; "screen_reset_encryption_password_title" = "Εισήγαγε τον κωδικό πρόσβασης του λογαριασμού σου για να συνεχίσεις"; "screen_reset_identity_confirmation_subtitle" = "Πρόκειται να μεταβείς στον λογαριασμό σου %1$@ για να επαναφέρεις την ταυτότητά σου. Στη συνέχεια, θα επιστρέψεις στην εφαρμογή."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Οι διαχειριστές έχουν αυτόματα δικαιώματα συντονιστή"; "screen_room_change_role_moderators_title" = "Επεξεργασία Συντονιστών"; "screen_room_change_role_unsaved_changes_description" = "Έχεις μη αποθηκευμένες αλλαγές."; -"screen_room_change_role_unsaved_changes_title" = "Αποθήκευση αλλαγών;"; "screen_room_details_add_topic_title" = "Προσθήκη θέματος"; "screen_room_details_already_a_member" = "Ήδη μέλος"; "screen_room_details_already_invited" = "Ήδη προσκεκλημένος"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Αποτυχία κατάργησης σίγασης αυτού του δωματίου, δοκίμασε ξανά."; "screen_room_details_notification_mode_custom" = "Προσαρμοσμένο"; "screen_room_details_notification_mode_default" = "Προεπιλογή"; -"screen_room_details_notification_title" = "Ειδοποιήσεις"; "screen_room_details_share_room_title" = "Κοινή χρήση δωματίου"; "screen_room_details_title" = "Πληροφορίες δωματίου"; "screen_room_details_updating_room" = "Ενημέρωση δωματίου..."; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Αποκλεισμός %1$@"; "screen_room_member_list_manage_member_ban" = "Αφαίρεση και αποκλεισμός μέλους"; "screen_room_member_list_manage_member_remove" = "Αφαίρεση από το δωμάτιο"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Αφαίρεση και αποκλεισμός μέλους"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Μόνο αφαίρεση μέλους"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Αφαίρεση μέλους και απαγόρευση συμμετοχής στο μέλλον;"; "screen_room_member_list_manage_member_unban_action" = "Αναίρεση αποκλεισμού"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Επισήμανση ως αναγνωσμένου"; "screen_roomlist_mark_as_unread" = "Επισήμανση ως μη αναγνωσμένου"; "screen_roomlist_room_directory_button_title" = "Περιήγηση σε όλα τα δωμάτια"; -"screen_server_confirmation_change_server" = "Αλλαγή παρόχου λογαριασμού"; "screen_server_confirmation_message_login_element_dot_io" = "Ένας ιδιωτικός διακομιστής για υπαλλήλους του Element."; "screen_server_confirmation_message_login_matrix_dot_org" = "Το Matrix είναι ένα ανοιχτό δίκτυο για ασφαλή, αποκεντρωμένη επικοινωνία."; "screen_server_confirmation_message_register" = "Εδώ θα ζουν οι συνομιλίες σου - όπως θα χρησιμοποιούσες έναν πάροχο email για να διατηρήσεις τα email σου."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Πρόκειται να αποσυνδεθείς από την τελευταία σου συνεδρία. Εάν αποσυνδεθείς τώρα, θα χάσεις την πρόσβαση στα κρυπτογραφημένα μηνύματά σου."; "screen_signout_key_backup_disabled_title" = "Έχεις απενεργοποιήσει τη δημιουργία αντιγράφων ασφαλείας"; "screen_signout_key_backup_offline_subtitle" = "Εξακολουθούσε να δημιουργείται αντίγραφο ασφαλείας των κλειδιών σου όταν βρέθηκες εκτός σύνδεσης. Επανασυνδέσου, ώστε να είναι δυνατή η δημιουργία αντιγράφων ασφαλείας των κλειδιών σου πριν αποσυνδεθείς."; -"screen_signout_key_backup_offline_title" = "Εξακολουθούν να δημιουργούνται αντίγραφα ασφαλείας των κλειδιών σου"; "screen_signout_key_backup_ongoing_subtitle" = "Περίμενε να ολοκληρωθεί πριν αποσυνδεθείς."; "screen_signout_key_backup_ongoing_title" = "Εξακολουθούν να δημιουργούνται αντίγραφα ασφαλείας των κλειδιών σου"; "screen_signout_recovery_disabled_subtitle" = "Πρόκειται να αποσυνδεθείς από την τελευταία σου συνεδρία. Εάν αποσυνδεθείς τώρα, θα χάσεις την πρόσβαση στα κρυπτογραφημένα μηνύματά σου."; "screen_signout_recovery_disabled_title" = "Η ανάκτηση δεν έχει ρυθμιστεί"; "screen_signout_save_recovery_key_subtitle" = "Πρόκειται να αποσυνδεθείς από την τελευταία σας συνεδρία. Εάν αποσυνδεθείς τώρα, ενδέχεται να χάσεις την πρόσβαση στα κρυπτογραφημένα μηνύματά σου."; -"screen_signout_save_recovery_key_title" = "Έχεις αποθηκεύσει το κλειδί ανάκτησης;"; "screen_start_chat_error_starting_chat" = "Παρουσιάστηκε σφάλμα κατά την προσπάθεια έναρξης μιας συνομιλίας"; "screen_view_location_title" = "Τοποθεσία"; "screen_welcome_bullet_1" = "Κλήσεις, δημοσκοπήσεις, αναζήτηση και άλλα, θα προστεθούν αργότερα φέτος."; @@ -920,7 +908,6 @@ "test_language_identifier" = "el"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Αντιμετώπιση προβλημάτων"; -"troubleshoot_notifications_entry_point_title" = "Αντιμετώπιση προβλημάτων ειδοποιήσεων"; "troubleshoot_notifications_screen_action" = "Εκτέλεση δοκιμών"; "troubleshoot_notifications_screen_action_again" = "Επανεκτέλεση δοκιμών"; "troubleshoot_notifications_screen_failure" = "Ορισμένες δοκιμές απέτυχαν. Παρακαλώ έλεγξε τις λεπτομέρειες."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Βεβαιώσου ότι οι διανομείς UnifiedPush είναι διαθέσιμοι."; "troubleshoot_notifications_test_unified_push_failure" = "Δεν βρέθηκαν διανομείς push."; "troubleshoot_notifications_test_unified_push_title" = "Έλεγχος UnifiedPush"; +"a11y_poll" = "Δημοσκόπηση"; "dialog_title_error" = "Σφάλμα"; "dialog_title_success" = "Επιτυχία"; "notification_fallback_content" = "Γνωστοποίηση"; "notification_invitation_action_join" = "Συμμετοχή"; +"notification_invitation_action_reject" = "Απόρριψη"; "notification_room_action_mark_as_read" = "Επισήμανση ως αναγνωσμένου"; "notification_room_action_quick_reply" = "Γρήγορη απάντηση"; +"screen_pinned_timeline_screen_title_empty" = "Καρφιτσωμένα μηνύματα"; "screen_room_mentions_at_room_title" = "Όλοι"; +"screen_account_provider_change" = "Αλλαγή παρόχου λογαριασμού"; "screen_account_provider_signin_subtitle" = "Εδώ θα ζουν οι συνομιλίες σου - όπως θα χρησιμοποιούσες έναν πάροχο email για να διατηρήσεις τα email σου."; "screen_account_provider_signup_subtitle" = "Εδώ θα ζουν οι συνομιλίες σου - όπως θα χρησιμοποιούσες έναν πάροχο email για να διατηρήσεις τα email σου."; "screen_analytics_settings_help_us_improve" = "Μοιράσου ανώνυμα δεδομένα χρήσης για να μάς βοηθήσεις να εντοπίσουμε προβλήματα."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Θα μπορείς να δεις ξανά όλα τα μηνύματα του."; "screen_blocked_users_unblock_alert_title" = "Κατάργηση αποκλεισμού χρήστη"; "screen_bug_report_rash_logs_alert_title" = "Το %1$@ διακόπηκε την τελευταία φορά που χρησιμοποιήθηκε. Θα 'θελες να μοιραστείς μια αναφορά σφάλματος μαζί μας;"; +"screen_chat_backup_recovery_action_confirm" = "Εισαγωγή κλειδιού ανάκτησης"; +"screen_create_poll_cancel_confirmation_content_ios" = "Οι αλλαγές σου δεν θα αποθηκευτούν"; "screen_create_room_add_people_title" = "Πρόσκληση ατόμων"; "screen_create_room_room_name_label" = "Όνομα δωματίου"; "screen_create_room_title" = "Δημιούργησε ένα δωμάτιο"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Επεξεργασία δημοσκόπησης"; "screen_identity_use_another_device" = "Χρήση άλλης συσκευής"; "screen_login_subtitle" = "Το Matrix είναι ένα ανοιχτό δίκτυο για ασφαλή, αποκεντρωμένη επικοινωνία."; +"screen_notification_settings_mentions_section_title" = "Αναφορές"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Προσπάθησε ξανά"; +"screen_recovery_key_confirm_title" = "Εισήγαγε το κλειδί ανάκτησης"; "screen_report_content_block_user" = "Αποκλεισμός χρήστη"; +"screen_reset_encryption_password_placeholder" = "Εισαγωγή..."; "screen_room_attachment_source_camera_photo" = "Τράβηξε φωτογραφία"; "screen_room_change_permissions_everyone" = "Όλοι"; "screen_room_change_permissions_member_moderation" = "Συντονισμός μελών"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Διαχειριστές"; "screen_room_change_role_section_moderators" = "Συντονιστές"; "screen_room_change_role_section_users" = "Μέλη"; +"screen_room_change_role_unsaved_changes_title" = "Αποθήκευση αλλαγών;"; "screen_room_details_invite_people_title" = "Πρόσκληση ατόμων"; "screen_room_details_leave_conversation_title" = "Αποχώρηση από τη συζήτηση"; "screen_room_details_leave_room_title" = "Αποχώρηση από το δωμάτιο"; +"screen_room_details_notification_title" = "Ειδοποιήσεις"; "screen_room_details_roles_and_permissions" = "Ρόλοι και δικαιώματα"; "screen_room_details_room_name_label" = "Όνομα δωματίου"; "screen_room_details_security_title" = "Ασφάλεια"; "screen_room_details_topic_title" = "Θέμα"; "screen_room_error_failed_processing_media" = "Αποτυχία μεταφόρτωσης μέσου, δοκίμασε ξανά."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Αφαίρεση και αποκλεισμός μέλους"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Μόνο αναφορές και λέξεις-κλειδιά"; "screen_roomlist_filter_people" = "Άτομα"; +"screen_server_confirmation_change_server" = "Αλλαγή παρόχου λογαριασμού"; "screen_signout_confirmation_dialog_submit" = "Αποσύνδεση"; "screen_signout_confirmation_dialog_title" = "Αποσύνδεση"; +"screen_signout_key_backup_offline_title" = "Εξακολουθούν να δημιουργούνται αντίγραφα ασφαλείας των κλειδιών σου"; "screen_signout_preference_item" = "Αποσύνδεση"; +"screen_signout_save_recovery_key_title" = "Έχεις αποθηκεύσει το κλειδί ανάκτησης;"; +"troubleshoot_notifications_entry_point_title" = "Αντιμετώπιση προβλημάτων ειδοποιήσεων"; diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 413594832b..f2bc97b9a4 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Pause"; "a11y_pin_field" = "PIN field"; "a11y_play" = "Play"; -"a11y_poll" = "Poll"; "a11y_poll_end" = "Ended poll"; "a11y_react_with" = "React with %1$@"; "a11y_react_with_other_emojis" = "React with other emojis"; @@ -163,6 +162,7 @@ "common_modern" = "Modern"; "common_mute" = "Mute"; "common_no_results" = "No results"; +"common_no_room_name" = "No room name"; "common_offline" = "Offline"; "common_optic_id_ios" = "Optic ID"; "common_or" = "or"; @@ -171,6 +171,8 @@ "common_permalink" = "Permalink"; "common_permission" = "Permission"; "common_please_wait" = "Please wait…"; +"common_poll_end_confirmation" = "Are you sure you want to end this poll?"; +"common_poll_summary" = "Poll: %1$@"; "common_poll_total_votes" = "Total votes: %1$@"; "common_poll_undisclosed_text" = "Results will show after the poll has ended"; "common_privacy_policy" = "Privacy policy"; @@ -201,6 +203,7 @@ "common_settings" = "Settings"; "common_shared_location" = "Shared location"; "common_signing_out" = "Signing out"; +"common_something_went_wrong" = "Something went wrong"; "common_starting_chat" = "Starting chat…"; "common_sticker" = "Sticker"; "common_success" = "Success"; @@ -214,6 +217,7 @@ "common_topic_placeholder" = "What is this room about?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Unable to decrypt"; +"common_unable_to_decrypt_no_access" = "You don't have access to this message"; "common_unable_to_invite_message" = "Invites couldn't be sent to one or more users."; "common_unable_to_invite_title" = "Unable to send invite(s)"; "common_unlock" = "Unlock"; @@ -222,24 +226,21 @@ "common_username" = "Username"; "common_verification_cancelled" = "Verification cancelled"; "common_verification_complete" = "Verification complete"; +"common_verify_device" = "Verify device"; "common_video" = "Video"; "common_voice_message" = "Voice message"; "common_waiting" = "Waiting…"; "common_waiting_for_decryption_key" = "Waiting for this message"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "Do not show this again"; "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "Send to"; "common.you" = "You"; -"common_no_room_name" = "No room name"; -"common_poll_end_confirmation" = "Are you sure you want to end this poll?"; -"common_poll_summary" = "Poll: %1$@"; -"common_something_went_wrong" = "Something went wrong"; -"common_unable_to_decrypt_no_access" = "You don't have access to this message"; -"common_verify_device" = "Verify device"; "confirm_recovery_key_banner_message" = "Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup."; "confirm_recovery_key_banner_title" = "Enter your recovery key"; "crash_detection_dialog_content" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "In order to let the application use the camera, please grant the permission in the system settings."; "dialog_permission_generic" = "Please grant the permission in the system settings."; "dialog_permission_location_description_ios" = "Grant access in Settings -> Location."; @@ -292,7 +293,6 @@ "notification_channel_silent" = "Silent notifications"; "notification_incoming_call" = "Incoming call"; "notification_inline_reply_failed" = "** Failed to send - please open room"; -"notification_invitation_action_reject" = "Reject"; "notification_invite_body" = "Invited you to chat"; "notification_invite_body_with_sender" = "%1$@ invited you to chat"; "notification_mentioned_you_body" = "Mentioned you: %1$@"; @@ -336,7 +336,6 @@ "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_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; -"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; "screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; @@ -355,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "Change account provider"; "screen_account_provider_form_hint" = "Homeserver address"; "screen_account_provider_form_notice" = "Enter a search term or a domain address."; "screen_account_provider_form_subtitle" = "Search for a company, community, or private server."; @@ -433,7 +431,6 @@ "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_recovery_action_change" = "Change recovery key"; -"screen_chat_backup_recovery_action_confirm" = "Enter recovery key"; "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."; @@ -449,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Show results only after poll ends"; "screen_create_poll_anonymous_headline" = "Hide votes"; "screen_create_poll_answer_hint" = "Option %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Your changes won’t be saved"; "screen_create_poll_cancel_confirmation_title_ios" = "Cancel Poll"; "screen_create_poll_question_desc" = "Question or topic"; "screen_create_poll_question_hint" = "What is the poll about?"; @@ -481,7 +477,7 @@ "screen_edit_profile_updating_details" = "Updating profile…"; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history"; +"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -546,7 +542,6 @@ "screen_notification_settings_group_chats" = "Group chats"; "screen_notification_settings_invite_for_me_label" = "Invitations"; "screen_notification_settings_mentions_only_disclaimer" = "Your homeserver does not support this option in encrypted rooms, you may not get notified in some rooms."; -"screen_notification_settings_mentions_section_title" = "Mentions"; "screen_notification_settings_mode_all" = "All"; "screen_notification_settings_mode_mentions" = "Mentions"; "screen_notification_settings_notification_section_title" = "Notify me for"; @@ -618,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Enter…"; "screen_recovery_key_confirm_lost_recovery_key" = "Lost your recovery key?"; "screen_recovery_key_confirm_success" = "Recovery key confirmed"; -"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_recovery_key_copied_to_clipboard" = "Copied recovery key"; "screen_recovery_key_generating_key" = "Generating…"; "screen_recovery_key_save_action" = "Save recovery key"; @@ -638,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Yes, reset now"; "screen_reset_encryption_confirmation_alert_subtitle" = "This process is irreversible."; "screen_reset_encryption_confirmation_alert_title" = "Are you sure you want to reset your identity?"; -"screen_reset_encryption_password_placeholder" = "Enter…"; "screen_reset_encryption_password_subtitle" = "Confirm that you want to reset your identity."; "screen_reset_encryption_password_title" = "Enter your account password to continue"; "screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; @@ -671,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Admins automatically have moderator privileges"; "screen_room_change_role_moderators_title" = "Edit Moderators"; "screen_room_change_role_unsaved_changes_description" = "You have unsaved changes."; -"screen_room_change_role_unsaved_changes_title" = "Save changes?"; "screen_room_details_add_topic_title" = "Add topic"; "screen_room_details_already_a_member" = "Already a member"; "screen_room_details_already_invited" = "Already invited"; @@ -688,7 +680,6 @@ "screen_room_details_error_unmuting" = "Failed unmuting this room, please try again."; "screen_room_details_notification_mode_custom" = "Custom"; "screen_room_details_notification_mode_default" = "Default"; -"screen_room_details_notification_title" = "Notifications"; "screen_room_details_share_room_title" = "Share room"; "screen_room_details_title" = "Room info"; "screen_room_details_updating_room" = "Updating room…"; @@ -713,7 +704,6 @@ "screen_room_member_list_banning_user" = "Banning %1$@"; "screen_room_member_list_manage_member_ban" = "Remove and ban member"; "screen_room_member_list_manage_member_remove" = "Remove from room"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Remove and ban member"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Only remove member"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Remove member and ban from joining in the future?"; "screen_room_member_list_manage_member_unban_action" = "Unban"; @@ -792,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Mark as read"; "screen_roomlist_mark_as_unread" = "Mark as unread"; "screen_roomlist_room_directory_button_title" = "Browse all rooms"; -"screen_server_confirmation_change_server" = "Change account provider"; "screen_server_confirmation_message_login_element_dot_io" = "A private server for Element employees."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix is an open network for secure, decentralised communication."; "screen_server_confirmation_message_register" = "This is where your conversations will live — just like you would use an email provider to keep your emails."; @@ -832,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "You are about to sign out of your last session. If you sign out now, you will lose access to your encrypted messages."; "screen_signout_key_backup_disabled_title" = "You have turned off backup"; "screen_signout_key_backup_offline_subtitle" = "Your keys were still being backed up when you went offline. Reconnect so that your keys can be backed up before signing out."; -"screen_signout_key_backup_offline_title" = "Your keys are still being backed up"; "screen_signout_key_backup_ongoing_subtitle" = "Please wait for this to complete before signing out."; "screen_signout_key_backup_ongoing_title" = "Your keys are still being backed up"; "screen_signout_recovery_disabled_subtitle" = "You are about to sign out of your last session. If you sign out now, you'll lose access to your encrypted messages."; "screen_signout_recovery_disabled_title" = "Recovery not set up"; "screen_signout_save_recovery_key_subtitle" = "You are about to sign out of your last session. If you sign out now, you might lose access to your encrypted messages."; -"screen_signout_save_recovery_key_title" = "Have you saved your recovery key?"; "screen_start_chat_error_starting_chat" = "An error occurred when trying to start a chat"; "screen_view_location_title" = "Location"; "screen_welcome_bullet_1" = "Calls, polls, search and more will be added later this year."; @@ -921,7 +908,6 @@ "test_language_identifier" = "en"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Troubleshoot"; -"troubleshoot_notifications_entry_point_title" = "Troubleshoot notifications"; "troubleshoot_notifications_screen_action" = "Run tests"; "troubleshoot_notifications_screen_action_again" = "Run tests again"; "troubleshoot_notifications_screen_failure" = "Some tests failed. Please check the details."; @@ -963,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Ensure that UnifiedPush distributors are available."; "troubleshoot_notifications_test_unified_push_failure" = "No push distributors found."; "troubleshoot_notifications_test_unified_push_title" = "Check UnifiedPush"; +"a11y_poll" = "Poll"; "dialog_title_error" = "Error"; "dialog_title_success" = "Success"; "notification_fallback_content" = "Notification"; "notification_invitation_action_join" = "Join"; +"notification_invitation_action_reject" = "Reject"; "notification_room_action_mark_as_read" = "Mark as read"; "notification_room_action_quick_reply" = "Quick reply"; +"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_room_mentions_at_room_title" = "Everyone"; +"screen_account_provider_change" = "Change account provider"; "screen_account_provider_signin_subtitle" = "This is where your conversations will live — just like you would use an email provider to keep your emails."; "screen_account_provider_signup_subtitle" = "This is where your conversations will live — just like you would use an email provider to keep your emails."; "screen_analytics_settings_help_us_improve" = "Share anonymous usage data to help us identify issues."; @@ -979,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "You'll be able to see all messages from them again."; "screen_blocked_users_unblock_alert_title" = "Unblock user"; "screen_bug_report_rash_logs_alert_title" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; +"screen_chat_backup_recovery_action_confirm" = "Enter recovery key"; +"screen_create_poll_cancel_confirmation_content_ios" = "Your changes won’t be saved"; "screen_create_room_add_people_title" = "Invite people"; "screen_create_room_room_name_label" = "Room name"; "screen_create_room_title" = "Create a room"; @@ -992,8 +984,11 @@ "screen_edit_poll_title" = "Edit poll"; "screen_identity_use_another_device" = "Use another device"; "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_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Block user"; +"screen_reset_encryption_password_placeholder" = "Enter…"; "screen_room_attachment_source_camera_photo" = "Take photo"; "screen_room_change_permissions_everyone" = "Everyone"; "screen_room_change_permissions_member_moderation" = "Member moderation"; @@ -1002,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Admins"; "screen_room_change_role_section_moderators" = "Moderators"; "screen_room_change_role_section_users" = "Members"; +"screen_room_change_role_unsaved_changes_title" = "Save changes?"; "screen_room_details_invite_people_title" = "Invite people"; "screen_room_details_leave_conversation_title" = "Leave conversation"; "screen_room_details_leave_room_title" = "Leave room"; +"screen_room_details_notification_title" = "Notifications"; "screen_room_details_roles_and_permissions" = "Roles and permissions"; "screen_room_details_room_name_label" = "Room name"; "screen_room_details_security_title" = "Security"; "screen_room_details_topic_title" = "Topic"; "screen_room_error_failed_processing_media" = "Failed processing media to upload, please try again."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Remove and ban member"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Mentions and Keywords only"; "screen_roomlist_filter_people" = "People"; +"screen_server_confirmation_change_server" = "Change account provider"; "screen_signout_confirmation_dialog_submit" = "Sign out"; "screen_signout_confirmation_dialog_title" = "Sign out"; +"screen_signout_key_backup_offline_title" = "Your keys are still being backed up"; "screen_signout_preference_item" = "Sign out"; +"screen_signout_save_recovery_key_title" = "Have you saved your recovery key?"; +"troubleshoot_notifications_entry_point_title" = "Troubleshoot notifications"; diff --git a/ElementX/Resources/Localizations/es.lproj/Localizable.strings b/ElementX/Resources/Localizations/es.lproj/Localizable.strings index 9fc09d4489..6773767eff 100644 --- a/ElementX/Resources/Localizations/es.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/es.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Pausar"; "a11y_pin_field" = "Campo PIN"; "a11y_play" = "Reproducir"; -"a11y_poll" = "Encuesta"; "a11y_poll_end" = "Encuesta finalizada"; "a11y_react_with" = "Reacciona con %1$@"; "a11y_react_with_other_emojis" = "Reacciona con otros emojis"; @@ -41,6 +40,7 @@ "action_create" = "Crear"; "action_create_a_room" = "Crear una sala"; "action_deactivate" = "Deactivate"; +"action_deactivate_account" = "Deactivate account"; "action_decline" = "Rechazar"; "action_delete_poll" = "Eliminar encuesta"; "action_disable" = "Desactivar"; @@ -64,6 +64,7 @@ "action_leave" = "Salir"; "action_leave_conversation" = "Salir de la conversación"; "action_leave_room" = "Salir de la sala"; +"action_load_more" = "Cargar más"; "action_manage_account" = "Gestionar cuenta"; "action_manage_devices" = "Administrar dispositivos"; "action_message" = "Message"; @@ -93,6 +94,7 @@ "action_send_message" = "Enviar mensaje"; "action_share" = "Compartir"; "action_share_link" = "Compartir enlace"; +"action_show" = "Show"; "action_sign_in_again" = "Inicia sesión de nuevo"; "action_signout" = "Cerrar sesión"; "action_signout_anyway" = "Cerrar sesión de todos modos"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "View in timeline"; "action_view_source" = "Ver Fuente"; "action_yes" = "Sí"; -"action.load_more" = "Cargar más"; -"action_deactivate_account" = "Deactivate account"; "banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; @@ -162,6 +162,7 @@ "common_modern" = "Moderno"; "common_mute" = "Silenciar"; "common_no_results" = "No hay resultados"; +"common_no_room_name" = "No room name"; "common_offline" = "Sin conexión"; "common_optic_id_ios" = "Optic ID"; "common_or" = "o"; @@ -170,6 +171,8 @@ "common_permalink" = "Enlace permanente"; "common_permission" = "Permiso"; "common_please_wait" = "Please wait…"; +"common_poll_end_confirmation" = "¿Estás seguro de que quieres finalizar esta encuesta?"; +"common_poll_summary" = "Encuesta: %1$@"; "common_poll_total_votes" = "Total de votos: %1$@"; "common_poll_undisclosed_text" = "Los resultados se mostrarán una vez finalizada la encuesta"; "common_privacy_policy" = "Política de privacidad"; @@ -200,6 +203,7 @@ "common_settings" = "Ajustes"; "common_shared_location" = "Ubicación compartida"; "common_signing_out" = "Cerrando sesión"; +"common_something_went_wrong" = "Something went wrong"; "common_starting_chat" = "Iniciando chat…"; "common_sticker" = "Sticker"; "common_success" = "Terminado"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "¿De qué trata esta sala?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "No se puede descifrar"; +"common_unable_to_decrypt_no_access" = "You don't have access to this message"; "common_unable_to_invite_message" = "Las invitaciones no se pudieron enviar a uno o más usuarios."; "common_unable_to_invite_title" = "No se pudo enviar la(s) invitación(es)"; "common_unlock" = "Desbloquear"; @@ -221,24 +226,21 @@ "common_username" = "Usuario"; "common_verification_cancelled" = "Verificación cancelada"; "common_verification_complete" = "Verificación completada"; +"common_verify_device" = "Verificar dispositivo"; "common_video" = "Vídeo"; "common_voice_message" = "Mensaje de voz"; "common_waiting" = "Esperando…"; "common_waiting_for_decryption_key" = "Esperando este mensaje"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "Do not show this again"; "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "Send to"; "common.you" = "You"; -"common_no_room_name" = "No room name"; -"common_poll_end_confirmation" = "¿Estás seguro de que quieres finalizar esta encuesta?"; -"common_poll_summary" = "Encuesta: %1$@"; -"common_something_went_wrong" = "Something went wrong"; -"common_unable_to_decrypt_no_access" = "You don't have access to this message"; -"common_verify_device" = "Verificar dispositivo"; "confirm_recovery_key_banner_message" = "La copia de seguridad del chat no está sincronizada en este momento. Debes confirmar tu clave de recuperación para mantener el acceso a la copia de seguridad del chat."; "confirm_recovery_key_banner_title" = "Confirma tu clave de recuperación"; "crash_detection_dialog_content" = "%1$@ se cerró inesperadamente la última vez que se lo usaste. ¿Quieres compartir un informe de error con nosotros?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "Para permitir que la aplicación utilice la cámara, por favor concede el permiso en los ajustes del sistema."; "dialog_permission_generic" = "Por favor concede el permiso en los ajustes del sistema."; "dialog_permission_location_description_ios" = "Concede el permiso en Configuración -> Ubicación."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "Notificaciones silenciosas"; "notification_incoming_call" = "Incoming call"; "notification_inline_reply_failed" = "** No se ha podido enviar - por favor, abre la sala"; -"notification_invitation_action_reject" = "Rechazar"; "notification_invite_body" = "Te invitó a chatear"; "notification_invite_body_with_sender" = "%1$@ invited you to chat"; "notification_mentioned_you_body" = "Te mencionó: %1$@"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "URL no válida, asegúrate de incluir el protocolo (http/https) y la dirección correcta."; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; -"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; "screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "Cambiar el proveedor de la cuenta"; "screen_account_provider_form_hint" = "Dirección del servidor principal"; "screen_account_provider_form_notice" = "Introduzca un término de búsqueda o una dirección de dominio."; "screen_account_provider_form_subtitle" = "Busca una empresa, comunidad o servidor privado."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "La copia de seguridad garantiza que no pierdas tu historial de mensajes. %1$@."; "screen_chat_backup_key_backup_title" = "Copia de seguridad"; "screen_chat_backup_recovery_action_change" = "Cambiar la clave de recuperación"; -"screen_chat_backup_recovery_action_confirm" = "Confirmar clave de recuperación"; "screen_chat_backup_recovery_action_confirm_description" = "La copia de seguridad de tus chats no está sincronizada ahora mismo."; "screen_chat_backup_recovery_action_setup" = "Configurar la clave de recuperación"; "screen_chat_backup_recovery_action_setup_description" = "Accede a tus mensajes cifrados si pierdes todos tus dispositivos o cierras sesión de %1$@ en cualquier lugar."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Mostrar los resultados solo después de que finalice la encuesta"; "screen_create_poll_anonymous_headline" = "Ocultar votos"; "screen_create_poll_answer_hint" = "Opción %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Tus cambios no se guardarán"; "screen_create_poll_cancel_confirmation_title_ios" = "Cancelar Encuesta"; "screen_create_poll_question_desc" = "Pregunta o tema"; "screen_create_poll_question_hint" = "¿De qué trata la encuesta?"; @@ -480,7 +477,7 @@ "screen_edit_profile_updating_details" = "Actualizando perfil..."; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history"; +"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Chats grupales"; "screen_notification_settings_invite_for_me_label" = "Invitaciones"; "screen_notification_settings_mentions_only_disclaimer" = "Tu servidor principal no admite esta opción en salas cifradas, puede que no recibas notificaciones en algunas salas."; -"screen_notification_settings_mentions_section_title" = "Menciones"; "screen_notification_settings_mode_all" = "Todos"; "screen_notification_settings_mode_mentions" = "Menciones"; "screen_notification_settings_notification_section_title" = "Notificarme para"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Ingresar..."; "screen_recovery_key_confirm_lost_recovery_key" = "Lost your recovery key?"; "screen_recovery_key_confirm_success" = "Clave de recuperación confirmada"; -"screen_recovery_key_confirm_title" = "Confirma tu clave de recuperación"; "screen_recovery_key_copied_to_clipboard" = "Clave de recuperación copiada"; "screen_recovery_key_generating_key" = "Generando…"; "screen_recovery_key_save_action" = "Guardar clave de recuperación"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Yes, reset now"; "screen_reset_encryption_confirmation_alert_subtitle" = "This process is irreversible."; "screen_reset_encryption_confirmation_alert_title" = "Are you sure you want to reset your identity?"; -"screen_reset_encryption_password_placeholder" = "Enter…"; "screen_reset_encryption_password_subtitle" = "Confirm that you want to reset your identity."; "screen_reset_encryption_password_title" = "Enter your account password to continue"; "screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Admins automatically have moderator privileges"; "screen_room_change_role_moderators_title" = "Editar moderadores"; "screen_room_change_role_unsaved_changes_description" = "Tienes cambios sin guardar."; -"screen_room_change_role_unsaved_changes_title" = "¿Guardar cambios?"; "screen_room_details_add_topic_title" = "Añadir tema"; "screen_room_details_already_a_member" = "Ya eres miembro"; "screen_room_details_already_invited" = "Ya estás invitado"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Error al dejar de silenciar esta sala, por favor inténtalo de nuevo."; "screen_room_details_notification_mode_custom" = "Personalizado"; "screen_room_details_notification_mode_default" = "Por defecto"; -"screen_room_details_notification_title" = "Notificaciones"; "screen_room_details_share_room_title" = "Compartir sala"; "screen_room_details_title" = "Room info"; "screen_room_details_updating_room" = "Actualizando la sala..."; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Prohibiendo %1$@"; "screen_room_member_list_manage_member_ban" = "Eliminar y prohibir a un miembro"; "screen_room_member_list_manage_member_remove" = "Remover de la sala"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Eliminar y prohibir miembro"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Solo eliminar miembro"; "screen_room_member_list_manage_member_remove_confirmation_title" = "¿Eliminar al miembro y prohibirle unirse en el futuro?"; "screen_room_member_list_manage_member_unban_action" = "Anular la prohibición"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Marcar como leído"; "screen_roomlist_mark_as_unread" = "Marcar como no leído"; "screen_roomlist_room_directory_button_title" = "Browse all rooms"; -"screen_server_confirmation_change_server" = "Cambiar el proveedor de la cuenta"; "screen_server_confirmation_message_login_element_dot_io" = "Un servidor privado para los empleados de Element."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix es una red abierta para una comunicación segura y descentralizada."; "screen_server_confirmation_message_register" = "Aquí es donde se alojarán tus conversaciones — justo como utilizarías un proveedor de correo electrónico para guardar tus correos electrónicos."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Estás a punto de cerrar tu última sesión. Si cierras sesión ahora, perderás el acceso a tus mensajes cifrados."; "screen_signout_key_backup_disabled_title" = "Has desactivado la copia de seguridad"; "screen_signout_key_backup_offline_subtitle" = "Se estaba haciendo una copia de seguridad de tus claves cuando te desconectaste. Vuelve a conectarte para que se haga una copia de seguridad de tus claves antes de desconectarte."; -"screen_signout_key_backup_offline_title" = "Se está guardando una copia de seguridad de tus claves"; "screen_signout_key_backup_ongoing_subtitle" = "Espera a que se complete antes de cerrar sesión."; "screen_signout_key_backup_ongoing_title" = "Se sigue guardando una copia de seguridad de tus claves"; "screen_signout_recovery_disabled_subtitle" = "Estás a punto de cerrar tu última sesión. Si cierras sesión ahora, perderás el acceso a tus mensajes cifrados."; "screen_signout_recovery_disabled_title" = "La recuperación no está configurada"; "screen_signout_save_recovery_key_subtitle" = "Estás a punto de cerrar tu última sesión. Si cierras la sesión ahora, podrías perder el acceso a tus mensajes cifrados."; -"screen_signout_save_recovery_key_title" = "¿Has guardado tu clave de recuperación?"; "screen_start_chat_error_starting_chat" = "Se ha producido un error al intentar iniciar un chat"; "screen_view_location_title" = "Ubicación"; "screen_welcome_bullet_1" = "Las llamadas, las encuestas, la búsqueda y más se agregarán más adelante este año."; @@ -920,7 +908,6 @@ "test_language_identifier" = "es"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Troubleshoot"; -"troubleshoot_notifications_entry_point_title" = "Troubleshoot notifications"; "troubleshoot_notifications_screen_action" = "Run tests"; "troubleshoot_notifications_screen_action_again" = "Run tests again"; "troubleshoot_notifications_screen_failure" = "Some tests failed. Please check the details."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Ensure that UnifiedPush distributors are available."; "troubleshoot_notifications_test_unified_push_failure" = "No push distributors found."; "troubleshoot_notifications_test_unified_push_title" = "Check UnifiedPush"; +"a11y_poll" = "Encuesta"; "dialog_title_error" = "Error"; "dialog_title_success" = "Terminado"; "notification_fallback_content" = "Notificación"; "notification_invitation_action_join" = "Unirse"; +"notification_invitation_action_reject" = "Reject"; "notification_room_action_mark_as_read" = "Marcar como leído"; "notification_room_action_quick_reply" = "Respuesta rápida"; +"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_room_mentions_at_room_title" = "Todos"; +"screen_account_provider_change" = "Cambiar el proveedor de la cuenta"; "screen_account_provider_signin_subtitle" = "Aquí es donde se alojarán tus conversaciones — justo como utilizarías un proveedor de correo electrónico para guardar tus correos electrónicos."; "screen_account_provider_signup_subtitle" = "Aquí es donde se alojarán tus conversaciones — justo como utilizarías un proveedor de correo electrónico para guardar tus correos electrónicos."; "screen_analytics_settings_help_us_improve" = "Compartir datos de uso anónimos para ayudarnos a identificar problemas."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Podrás ver todos sus mensajes de nuevo."; "screen_blocked_users_unblock_alert_title" = "Desbloquear usuario"; "screen_bug_report_rash_logs_alert_title" = "%1$@ se cerró inesperadamente la última vez que se lo usaste. ¿Quieres compartir un informe de error con nosotros?"; +"screen_chat_backup_recovery_action_confirm" = "Introduzca la clave de recuperación"; +"screen_create_poll_cancel_confirmation_content_ios" = "Tus cambios no se guardarán"; "screen_create_room_add_people_title" = "Invitar personas"; "screen_create_room_room_name_label" = "Nombre de la sala"; "screen_create_room_title" = "Crear una sala"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Editar encuesta"; "screen_identity_use_another_device" = "Usar otro dispositivo"; "screen_login_subtitle" = "Matrix es una red abierta para una comunicación segura y descentralizada."; +"screen_notification_settings_mentions_section_title" = "Menciones"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Inténtalo de nuevo"; +"screen_recovery_key_confirm_title" = "Confirma tu clave de recuperación"; "screen_report_content_block_user" = "Bloquear usuario"; +"screen_reset_encryption_password_placeholder" = "Ingresar..."; "screen_room_attachment_source_camera_photo" = "Hacer foto"; "screen_room_change_permissions_everyone" = "Todos"; "screen_room_change_permissions_member_moderation" = "Moderación de miembros"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Administradores"; "screen_room_change_role_section_moderators" = "Moderadores"; "screen_room_change_role_section_users" = "Miembros"; +"screen_room_change_role_unsaved_changes_title" = "¿Guardar cambios?"; "screen_room_details_invite_people_title" = "Invitar personas"; "screen_room_details_leave_conversation_title" = "Salir de la conversación"; "screen_room_details_leave_room_title" = "Salir de la sala"; +"screen_room_details_notification_title" = "Notificaciones"; "screen_room_details_roles_and_permissions" = "Roles y permisos"; "screen_room_details_room_name_label" = "Nombre de la sala"; "screen_room_details_security_title" = "Seguridad"; "screen_room_details_topic_title" = "Tema"; "screen_room_error_failed_processing_media" = "Error al procesar el contenido multimedia, por favor inténtalo de nuevo."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Eliminar y prohibir a un miembro"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Únicamente Menciones y Palabras clave"; "screen_roomlist_filter_people" = "Personas"; +"screen_server_confirmation_change_server" = "Cambiar el proveedor de la cuenta"; "screen_signout_confirmation_dialog_submit" = "Cerrar sesión"; "screen_signout_confirmation_dialog_title" = "Cerrar sesión"; +"screen_signout_key_backup_offline_title" = "Se sigue guardando una copia de seguridad de tus claves"; "screen_signout_preference_item" = "Cerrar sesión"; +"screen_signout_save_recovery_key_title" = "¿Has guardado tu clave de recuperación?"; +"troubleshoot_notifications_entry_point_title" = "Troubleshoot notifications"; diff --git a/ElementX/Resources/Localizations/et.lproj/Localizable.strings b/ElementX/Resources/Localizations/et.lproj/Localizable.strings index 4d814ab2b0..ea8ee03b8b 100644 --- a/ElementX/Resources/Localizations/et.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/et.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Peata"; "a11y_pin_field" = "PIN-koodi väli"; "a11y_play" = "Esita"; -"a11y_poll" = "Küsitlus"; "a11y_poll_end" = "Lõppenud küsitlus"; "a11y_react_with" = "Reageeri emotikoniga %1$@"; "a11y_react_with_other_emojis" = "Reageeri mõne muu emotikoniga"; @@ -41,6 +40,7 @@ "action_create" = "Loo"; "action_create_a_room" = "Loo jututuba"; "action_deactivate" = "Eemalda konto"; +"action_deactivate_account" = "Eemalda konto kasutusest"; "action_decline" = "Keeldu"; "action_delete_poll" = "Kustuta küsitlus"; "action_disable" = "Lülita välja"; @@ -64,6 +64,7 @@ "action_leave" = "Lahku"; "action_leave_conversation" = "Lahku vestlusest"; "action_leave_room" = "Lahku jututoast"; +"action_load_more" = "Näita veel"; "action_manage_account" = "Halda kasutajakontot"; "action_manage_devices" = "Halda seadmeid"; "action_message" = "Saada sõnum"; @@ -93,6 +94,7 @@ "action_send_message" = "Saada sõnum"; "action_share" = "Jaga"; "action_share_link" = "Jaga linki"; +"action_show" = "Näita"; "action_sign_in_again" = "Logi uuesti sisse"; "action_signout" = "Logi välja"; "action_signout_anyway" = "Ikkagi logi välja"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "Vaata ajajoonel"; "action_view_source" = "Vaata lähtekoodi"; "action_yes" = "Jah"; -"action.load_more" = "Näita veel"; -"action_deactivate_account" = "Eemalda konto kasutusest"; "banner_migrate_to_native_sliding_sync_action" = "Logi välja ja uuenda"; "banner_migrate_to_native_sliding_sync_description" = "Sinu koduserver toetab uut ja kiiremat protokolli. Uuendamiseks logi korraks rakendusest välja ja siis tagasi. Mingil hetkel tulevikus vana protokoll eemaldatakse kasutusest ja tehes uuenduse nüüd väldid hilisemat sundkorras uuendust."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Sinu koduserver enam ei toeta vana protokolli. Jätkamaks rakenduse kasutamist palun logi välja ning seejärel tagasi."; @@ -162,6 +162,7 @@ "common_modern" = "Kaasaegne"; "common_mute" = "Summutatud"; "common_no_results" = "Otsingul pole tulemusi"; +"common_no_room_name" = "Jututoal puudub nimi"; "common_offline" = "Võrgust väljas"; "common_optic_id_ios" = "Optic ID"; "common_or" = "või"; @@ -170,6 +171,8 @@ "common_permalink" = "Püsilink"; "common_permission" = "Õigus"; "common_please_wait" = "Palun oota…"; +"common_poll_end_confirmation" = "Kas oled kindel, et soovid selle küsitluse lõpetada?"; +"common_poll_summary" = "Küsitlus: %1$@"; "common_poll_total_votes" = "Hääli kokku: %1$@"; "common_poll_undisclosed_text" = "Tulemused on näha peale küsitluse lõppemist"; "common_privacy_policy" = "Privaatsuspoliitika"; @@ -200,6 +203,7 @@ "common_settings" = "Seadistused"; "common_shared_location" = "Jagatud asukoht"; "common_signing_out" = "Logime välja"; +"common_something_went_wrong" = "Midagi läks valesti"; "common_starting_chat" = "Alustame vestlust…"; "common_sticker" = "Kleeps"; "common_success" = "Õnnestus"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "Mis on selle jututoa mõte?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Dekrüptimine ei olnud võimalik"; +"common_unable_to_decrypt_no_access" = "Sul pole ligipääsu antud sõnumile"; "common_unable_to_invite_message" = "Kutset polnud võimalik saata ühele või enamale kasutajale."; "common_unable_to_invite_title" = "Kutse(te) saatmine ei õnnestunud"; "common_unlock" = "Eemalda lukustus"; @@ -221,24 +226,21 @@ "common_username" = "Kasutajanimi"; "common_verification_cancelled" = "Verifitseerimine on katkestatud"; "common_verification_complete" = "Verifitseerimine on tehtud"; +"common_verify_device" = "Verifitseeri seade"; "common_video" = "Video"; "common_voice_message" = "Häälsõnum"; "common_waiting" = "Ootame…"; "common_waiting_for_decryption_key" = "Ootame selle sõnumi dekrüptimisvõtit"; +"common.copied_to_clipboard" = "Kopeeritud lõikelauale"; "common.do_not_show_this_again" = "Ära enam näita seda uuesti"; "common.open_source_licenses" = "Avatud lähtekoodiga litsentsid"; "common.pinned" = "Esiletõstetud"; "common.send_to" = "Saada kasutajale"; "common.you" = "Sina"; -"common_no_room_name" = "Jututoal puudub nimi"; -"common_poll_end_confirmation" = "Kas oled kindel, et soovid selle küsitluse lõpetada?"; -"common_poll_summary" = "Küsitlus: %1$@"; -"common_something_went_wrong" = "Midagi läks valesti"; -"common_unable_to_decrypt_no_access" = "Sul pole ligipääsu antud sõnumile"; -"common_verify_device" = "Verifitseeri seade"; "confirm_recovery_key_banner_message" = "Sinu vestluste varukoopia pole hetkel sünkroonis. Säilitamaks ligipääsu vestluse varukoopiale palun sisesta oma taastevõti."; "confirm_recovery_key_banner_title" = "Sisesta oma taastevõti"; "crash_detection_dialog_content" = "%1$@ jooksis kokku viimati, kui seda kasutasid. Kas tahaksid selle kohta meile veateate saata?"; +"crypto_identity_change_pin_violation" = "Kasutaja %1$@ võrguidentiteet tundub olema muutunud. %2$@"; "dialog_permission_camera" = "Selleks, et rakendus saaks kaamerat kasutada, palun luba see süsteemi seadistuses."; "dialog_permission_generic" = "Palun luba süsteemi seadistustest vajalikud õigused."; "dialog_permission_location_description_ios" = "Luba õigused Seadistused -> Asukoht valikust."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "Vaiksed teavitused"; "notification_incoming_call" = "Sissetulev kõne"; "notification_inline_reply_failed" = "** Saatmine ei õnnestunud - palun ava jututoa täisvaade"; -"notification_invitation_action_reject" = "Lükka tagasi"; "notification_invite_body" = "Kutse osalema vestluses"; "notification_invite_body_with_sender" = "%1$@ saatus sulle vestluskutse"; "notification_mentioned_you_body" = "Mainis sind: %1$@"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "Vigane url. Palun vaata, et url algaks protokolliga (http/https) ning aadress ise oleks ka õige."; "screen_pinned_timeline_empty_state_description" = "Siia lisamiseks vajuta sõnumil ja vali „%1$@“."; "screen_pinned_timeline_empty_state_headline" = "Et olulisi sõnumeid oleks lihtsam leida, tõsta nad esile"; -"screen_pinned_timeline_screen_title_empty" = "Esiletõstetud sõnumid"; "screen_reset_encryption_password_error" = "Tekkis teadmata viga. Palun kontrolli, kas sinu kasutajakonto salasõna on õige ja proovi uuesti."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Unusta verifitseerimine ja saada ikkagi"; "screen_resolve_send_failure_changed_identity_subtitle" = "Sa võid jätta verifitseerimisvea tähelepanuta ja sõnumi ikkagi saata või katkestad saatmise ja peale kasutaja %1$@ verifitseerimist proovid seda uuesti."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Sõnum on saatmata, kuna kasutaja %1$@ verifitseeritud identiteet on muutunud."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Sõnum on saatmata, kuna %1$@ pole verifitseerinud kõiki oma seadmeid."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Kuna sa pole üks või enamgi oma seadet verifitseerinud, siis sinu sõnum on saatmata."; -"screen_account_provider_change" = "Muuda teenusepakkujat"; "screen_account_provider_form_hint" = "Koduserveri aadress"; "screen_account_provider_form_notice" = "Sisesta otsingusõna või domeeni nimi."; "screen_account_provider_form_subtitle" = "Otsi äriühingut, kogukonda või võrgus leiduvat Matrixi serverit."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "Varundamine tagab, et sinu sõnumite ajalugu on alati loetav. %1$@."; "screen_chat_backup_key_backup_title" = "Varundus"; "screen_chat_backup_recovery_action_change" = "Muuda taastevõtit"; -"screen_chat_backup_recovery_action_confirm" = "Sisesta oma taastevõti"; "screen_chat_backup_recovery_action_confirm_description" = "Sinu vestluste krüptograafia varukoopia pole hetkel enam sünkroonis."; "screen_chat_backup_recovery_action_setup" = "Seadista krüptovõtmete varundus"; "screen_chat_backup_recovery_action_setup_description" = "Säilita ligipääs oma krüptitud sõnumitele ka siis, kui sa kaotad kõik oma seadmed ja/või logid kõikjal välja rakendusest %1$@."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Näita tulemusi alles pärast küsitluse lõppu"; "screen_create_poll_anonymous_headline" = "Peida hääled"; "screen_create_poll_answer_hint" = "Valik %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Sinu muudatused jäävad salvestamata"; "screen_create_poll_cancel_confirmation_title_ios" = "Tühista küsitlus"; "screen_create_poll_question_desc" = "Küsimus või teema"; "screen_create_poll_question_hint" = "Mis on küsitluse teema?"; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Rühmavestlused"; "screen_notification_settings_invite_for_me_label" = "Kutsed"; "screen_notification_settings_mentions_only_disclaimer" = "Sinu koduserver ei toeta seda funktsionaalsust krüptitud jututubades ja seega ei pruugi kõik teavitused sinuni jõuda."; -"screen_notification_settings_mentions_section_title" = "Mainimised"; "screen_notification_settings_mode_all" = "Kõik"; "screen_notification_settings_mode_mentions" = "Mainimiste alusel"; "screen_notification_settings_notification_section_title" = "Teavita mind"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Sisesta..."; "screen_recovery_key_confirm_lost_recovery_key" = "Kas sa oled taastevõtme kaotanud?"; "screen_recovery_key_confirm_success" = "Taastevõti on kinnitatud"; -"screen_recovery_key_confirm_title" = "Sisesta oma taastevõti"; "screen_recovery_key_copied_to_clipboard" = "Taastevõti on kopeeritud lõikelauale"; "screen_recovery_key_generating_key" = "Loome..."; "screen_recovery_key_save_action" = "Salvesta taastevõti"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Jah, lähtesta nüüd"; "screen_reset_encryption_confirmation_alert_subtitle" = "See tegevus on tagasipöördumatu."; "screen_reset_encryption_confirmation_alert_title" = "Kas sa oled kindel, et soovid oma andmete krüptimist lähtestada?"; -"screen_reset_encryption_password_placeholder" = "Sisesta..."; "screen_reset_encryption_password_subtitle" = "Palun kinnita, et soovid oma andmete krüptimist lähtestada."; "screen_reset_encryption_password_title" = "Jätkamaks sisesta oma kasutajakonto salasõna"; "screen_reset_identity_confirmation_subtitle" = "Oma võrguidentiteedi lähtestamiseks suuname sind %1$@ kasutajakonto halduse lehele. Hiljem suunatakse sind tagasi sama rakenduse juurde."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Peakasutajatel on automaatselt ka moderaatori õigused"; "screen_room_change_role_moderators_title" = "Muuda moderaatoreid"; "screen_room_change_role_unsaved_changes_description" = "Sul on salvestamata muudatusi"; -"screen_room_change_role_unsaved_changes_title" = "Kas salvestame muudatused?"; "screen_room_details_add_topic_title" = "Lisa teema"; "screen_room_details_already_a_member" = "Sa juba oled jututoa liige"; "screen_room_details_already_invited" = "Sa juba oled kutse saanud"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Selle jututoa summutamise eemaldamine ei õnnestunud. Palun proovi uuesti."; "screen_room_details_notification_mode_custom" = "Kohandatud"; "screen_room_details_notification_mode_default" = "Vaikimisi"; -"screen_room_details_notification_title" = "Teavitused"; "screen_room_details_share_room_title" = "Jaga jututuba"; "screen_room_details_title" = "Jututoa teave"; "screen_room_details_updating_room" = "Uuendame jututuba…"; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Seame kasutajale %1$@ suhtluskeelu"; "screen_room_member_list_manage_member_ban" = "Eemalda ja sea suhtluskeeld"; "screen_room_member_list_manage_member_remove" = "Eemalda kasutaja jututoast"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Eemalda kasutaja jututoast ja sea talle suhtluskeeld"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Ainult eemalda kasutaja"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Kas eemaldama kasutaja ja seame talle tulevikuks suhtluskeelu?"; "screen_room_member_list_manage_member_unban_action" = "Eemalda suhtluskeeld"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Märgi loetuks"; "screen_roomlist_mark_as_unread" = "Märgi mitteloetuks"; "screen_roomlist_room_directory_button_title" = "Sirvi kõiki jututube"; -"screen_server_confirmation_change_server" = "Muuda teenusepakujat"; "screen_server_confirmation_message_login_element_dot_io" = "Privaatne server Elemendi töötajate jaoks."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix on avatud võrk turvalise ja hajutatud suhtluse jaoks."; "screen_server_confirmation_message_register" = "See on koht, kus sinu vestlused elavad – just nagu kasutaksid oma e-kirjade säilitamiseks e-postiteenuse pakkujat."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Oled oma viimasest seansist välja logimas. Kui logid nüüd välja, kaotad ligipääsu oma krüptitud sõnumitele."; "screen_signout_key_backup_disabled_title" = "Sa oled varukoopiate tegemise välja lülitanud"; "screen_signout_key_backup_offline_subtitle" = "Kui su võrguühendus katkes, siis sinu krüptovõtmed oli parasjagu varundamisel. Loo võrguühendus uuesti, oota kuni krüptovõtmete varundamine lõppeb ja alles siis logi rakendusest välja."; -"screen_signout_key_backup_offline_title" = "Sinu krüptovõtmed on veel varundamisel"; "screen_signout_key_backup_ongoing_subtitle" = "Enne väljalogimist palun oota, et pooleliolev toiming lõppeb."; "screen_signout_key_backup_ongoing_title" = "Sinu krüptovõtmed on veel varundamisel"; "screen_signout_recovery_disabled_subtitle" = "Sa oled logimas välja oma viimasest sessioonist. Kui teed seda nüüd, siis kaotad ligipääsu oma krüptitud sõnumitele."; "screen_signout_recovery_disabled_title" = "Andmete taastamine on seadistamata"; "screen_signout_save_recovery_key_subtitle" = "Sa oled logimas välja oma viimasest sessioonist. Kui teed seda nüüd, siis ilmselt kaotad ligipääsu oma krüptitud sõnumitele."; -"screen_signout_save_recovery_key_title" = "Kas sa oled oma taastevõtme salvestanud?"; "screen_start_chat_error_starting_chat" = "Vestluse alustamisel tekkis viga"; "screen_view_location_title" = "Asukoht"; "screen_welcome_bullet_1" = "Kõned, küsitlused, otsing ja palju muud lisanduvad hiljem selle aasta jooksul."; @@ -920,7 +908,6 @@ "test_language_identifier" = "et"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Veaotsing"; -"troubleshoot_notifications_entry_point_title" = "Teavituste veaotsing"; "troubleshoot_notifications_screen_action" = "Käivita testid"; "troubleshoot_notifications_screen_action_again" = "Käivita testid uuesti"; "troubleshoot_notifications_screen_failure" = "Mõned testid tuvastasid vigu. Palun vaata üksikasjalikku teavet."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Palun veendu, et UnifiedPushi levitajad on saadaval."; "troubleshoot_notifications_test_unified_push_failure" = "Tõuketeenuse levitajaid ei leidu."; "troubleshoot_notifications_test_unified_push_title" = "Kontrolli UnifiedPushi"; +"a11y_poll" = "Küsitlus"; "dialog_title_error" = "Viga"; "dialog_title_success" = "Õnnestus"; "notification_fallback_content" = "Teavitus"; "notification_invitation_action_join" = "Liitu"; +"notification_invitation_action_reject" = "Keeldu"; "notification_room_action_mark_as_read" = "Märgi loetuks"; "notification_room_action_quick_reply" = "Kiirvastus"; +"screen_pinned_timeline_screen_title_empty" = "Esiletõstetud sõnumid"; "screen_room_mentions_at_room_title" = "Kõik"; +"screen_account_provider_change" = "Muuda teenusepakkujat"; "screen_account_provider_signin_subtitle" = "See on koht, kus sinu vestlused elavad – just nagu kasutaksid oma e-kirjade säilitamiseks e-postiteenuse pakkujat."; "screen_account_provider_signup_subtitle" = "See on koht, kus sinu vestlused elavad – just nagu kasutaksid oma e-kirjade säilitamiseks e-postiteenuse pakkujat."; "screen_analytics_settings_help_us_improve" = "Võimalike rakenduse vigade leidmiseks palun jaga anonüümset kasutusteavet."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Nüüd näed sa jälle kõiki tema sõnumeid"; "screen_blocked_users_unblock_alert_title" = "Eemalda kasutajalt blokeering"; "screen_bug_report_rash_logs_alert_title" = "%1$@ jooksis kokku viimati, kui seda kasutasid. Kas tahaksid selle kohta meile veateate saata?"; +"screen_chat_backup_recovery_action_confirm" = "Sisesta taastevõti"; +"screen_create_poll_cancel_confirmation_content_ios" = "Sinu tehtud muudatused jäävad salvestamata"; "screen_create_room_add_people_title" = "Kutsu osalejaid"; "screen_create_room_room_name_label" = "Jututoa nimi"; "screen_create_room_title" = "Loo jututuba"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Muuda küsitlust"; "screen_identity_use_another_device" = "Kasuta teist seadet"; "screen_login_subtitle" = "Matrix on avatud võrk turvalise ja hajutatud suhtluse jaoks."; +"screen_notification_settings_mentions_section_title" = "Mainimiste alusel"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Proovi uuesti"; +"screen_recovery_key_confirm_title" = "Sisesta oma taastevõti"; "screen_report_content_block_user" = "Blokeeri kasutaja"; +"screen_reset_encryption_password_placeholder" = "Sisesta..."; "screen_room_attachment_source_camera_photo" = "Tee pilt"; "screen_room_change_permissions_everyone" = "Kõik"; "screen_room_change_permissions_member_moderation" = "Jututoas osalejate modereerimine"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Peakasutajad"; "screen_room_change_role_section_moderators" = "Moderaatorid"; "screen_room_change_role_section_users" = "Liikmed"; +"screen_room_change_role_unsaved_changes_title" = "Kas salvestame muudatused?"; "screen_room_details_invite_people_title" = "Kutsu osalejaid"; "screen_room_details_leave_conversation_title" = "Lahku vestlusest"; "screen_room_details_leave_room_title" = "Lahku jututoast"; +"screen_room_details_notification_title" = "Teavitused"; "screen_room_details_roles_and_permissions" = "Rollid ja õigused"; "screen_room_details_room_name_label" = "Jututoa nimi"; "screen_room_details_security_title" = "Turvalisus"; "screen_room_details_topic_title" = "Teema"; "screen_room_error_failed_processing_media" = "Meediafaili töötlemine enne üleslaadimist ei õnnestunud. Palun proovi uuesti."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Eemalda ja sea suhtluskeeld"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Mainimiste ja võtmesõnade alusel"; "screen_roomlist_filter_people" = "Inimesed"; +"screen_server_confirmation_change_server" = "Muuda teenusepakkujat"; "screen_signout_confirmation_dialog_submit" = "Logi välja"; "screen_signout_confirmation_dialog_title" = "Logi välja"; +"screen_signout_key_backup_offline_title" = "Sinu krüptovõtmed on veel varundamisel"; "screen_signout_preference_item" = "Logi välja"; +"screen_signout_save_recovery_key_title" = "Kas sa oled oma taastevõtme talletanud?"; +"troubleshoot_notifications_entry_point_title" = "Teavituste veaotsing"; diff --git a/ElementX/Resources/Localizations/fa.lproj/Localizable.strings b/ElementX/Resources/Localizations/fa.lproj/Localizable.strings new file mode 100644 index 0000000000..e8d4f5b4c9 --- /dev/null +++ b/ElementX/Resources/Localizations/fa.lproj/Localizable.strings @@ -0,0 +1,1019 @@ +"Notification" = "آگاهی"; +"a11y_delete" = "حذف"; +"a11y_hide_password" = "نهفتن گذرواژه"; +"a11y_jump_to_bottom" = "به پایین بروید"; +"a11y_notifications_mentions_only" = "فقط اشاره‌ها"; +"a11y_notifications_muted" = "خموش"; +"a11y_page_n" = "صفحهٔ %1$d"; +"a11y_pause" = "مکث"; +"a11y_pin_field" = "زمینهٔ پین"; +"a11y_play" = "پخش"; +"a11y_poll_end" = "نظرسنجی پایان یافته"; +"a11y_react_with" = "واکنش با %1$@"; +"a11y_react_with_other_emojis" = "واکنش با شکلک‌های دیگر"; +"a11y_read_receipts_multiple" = "خوانده به دست %1$@ و %2$@"; +"a11y_read_receipts_single" = "خوانده به دست %1$@"; +"a11y_read_receipts_tap_to_show_all" = "زدن برای نمایش همه"; +"a11y_remove_reaction_with" = "برداشتن واکنش با %1$@"; +"a11y_send_files" = "ارسال پرونده‌ها"; +"a11y_show_password" = "نمایش گذرواژه"; +"a11y_start_call" = "آغاز یک تماس"; +"a11y_user_menu" = "فهرست کاربر"; +"a11y_voice_message_record" = "ضبط پیام صوتی."; +"a11y_voice_message_stop_recording" = "توقّف ضبط"; +"action_accept" = "پذیرش"; +"action_add_to_timeline" = "افزودن به خط زمانی"; +"action_back" = "بازگشت"; +"action_call" = "تماس"; +"action_cancel" = "لغو"; +"action_cancel_for_now" = "لغو برای امنون"; +"action_choose_photo" = "گزینش عکس"; +"action_clear" = "پاک سازی"; +"action_close" = "بستن"; +"action_complete_verification" = "تکمیل تأیید"; +"action_confirm" = "تأیید"; +"action_confirm_password" = "تأیید گذرواژه"; +"action_continue" = "ادامه"; +"action_copy" = "رونوشت"; +"action_copy_link" = "رونوشت از پیوند"; +"action_copy_link_to_message" = "رونوشت از پیوند پیام"; +"action_create" = "ایجاد"; +"action_create_a_room" = "ایجاد اتاق"; +"action_deactivate" = "غیرفعّال"; +"action_deactivate_account" = "غیرفعّال‌سازی حساب"; +"action_decline" = "رد"; +"action_delete_poll" = "حذف نظرسنجی"; +"action_disable" = "از کار انداختن"; +"action_discard" = "دور ریختن"; +"action_done" = "انجام شد"; +"action_edit" = "ویرایش"; +"action_edit_poll" = "ویرایش نظرسنجی"; +"action_enable" = "به کار انداختن"; +"action_end_poll" = "پایان نظرسنجی"; +"action_enter_pin" = "ورود پین"; +"action_forgot_password" = "گذرواژه را فراموش کردید؟"; +"action_forward" = "پیشروی"; +"action_go_back" = "پس‌روی"; +"action_invite" = "دعوت"; +"action_invite_friends" = "دعوت افراد"; +"action_invite_friends_to_app" = "دعوت به %1$@"; +"action_invite_people_to_app" = "دعوت افراد به %1$@"; +"action_invites_list" = "دعوت‌ها"; +"action_join" = "پیوستن"; +"action_learn_more" = "بیش‌تر دانستن"; +"action_leave" = "ترک"; +"action_leave_conversation" = "ترک گفت‌وگو"; +"action_leave_room" = "ترک اتاق"; +"action_load_more" = "بار کردن بیش‌تر"; +"action_manage_account" = "مدیریت حساب"; +"action_manage_devices" = "مدیریت افزاره‌ها"; +"action_message" = "پیام"; +"action_next" = "بعدی"; +"action_no" = "نه"; +"action_not_now" = "الآن نه"; +"action_ok" = "قبول"; +"action_open_settings" = "تنظیمات"; +"action_open_with" = "گشودن با"; +"action_pin" = "سنجاق"; +"action_quick_reply" = "پاسخ سریع"; +"action_quote" = "نقل قول"; +"action_react" = "واکنش"; +"action_reject" = "رد کردن"; +"action_remove" = "برداشتن"; +"action_reply" = "پاسخ"; +"action_reply_in_thread" = "پاسخ در رشته"; +"action_report_bug" = "گزارش اشکال"; +"action_report_content" = "گزارش محتوا"; +"action_reset" = "بازنشانی"; +"action_reset_identity" = "بازنشانی هویت"; +"action_retry" = "تلاش دوباره"; +"action_retry_decryption" = "تلاش دوباره برای رمزگشایی"; +"action_save" = "ذخیره"; +"action_search" = "جست‌وجو"; +"action_send" = "فرستادن"; +"action_send_message" = "فرستادن پیام"; +"action_share" = "هم‌رسانی"; +"action_share_link" = "هم‌رسانی پیوند"; +"action_show" = "نمایش"; +"action_sign_in_again" = "ورود دوباره"; +"action_signout" = "خروج"; +"action_signout_anyway" = "خروج به هر صورت"; +"action_skip" = "پرش"; +"action_start" = "آغاز"; +"action_start_chat" = "آغاز گپ"; +"action_start_verification" = "آغاز تأیید"; +"action_static_map_load" = "زدن برای بار کردن نقشه"; +"action_take_photo" = "عکس گرفتن"; +"action_tap_for_options" = "زدن برای گزینه‌ها"; +"action_try_again" = "تلاش دوباره"; +"action_unpin" = "سنجاق نکردن"; +"action_view_in_timeline" = "دیدن در خط زمانی"; +"action_view_source" = "دیدن منبع"; +"action_yes" = "بله"; +"banner_migrate_to_native_sliding_sync_action" = "خروج و ارتقا"; +"banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; +"banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; +"banner_migrate_to_native_sliding_sync_title" = "ارتقا موجود است"; +"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; +"banner.set_up_recovery.title" = "برپایی بازیابی"; +"common_about" = "درباره"; +"common_acceptable_use_policy" = "سیاست استفادهٔ پذیرفتنی"; +"common_advanced_settings" = "تنظیمات پیش‌رفته"; +"common_analytics" = "تجزیه و تحلیل"; +"common_appearance" = "ظاهر"; +"common_audio" = "صدا"; +"common_blocked_users" = "کاربران مسدود"; +"common_bubbles" = "حباب‌ها"; +"common_call_invite" = "تماس در جریان (پشتیبانی نشده)"; +"common_call_started" = "تماس آغاز شد"; +"common_chat_backup" = "پشتیبان گپ"; +"common_copyright" = "حق رونوشت"; +"common_creating_room" = "ایجاد کردن اتاق…"; +"common_current_user_left_room" = "اتاق را ترک کرد"; +"common_dark" = "تیره"; +"common_decryption_error" = "خطای رمزگشایی"; +"common_developer_options" = "گزینه‌های توسعه دهنده"; +"common_direct_chat" = "گپ مستقیم"; +"common_edited_suffix" = "(ویراسته)"; +"common_editing" = "ویرایش"; +"common_emote" = "* %1$@ %2$@"; +"common_encryption_enabled" = "رمزنگاری به کار افتاده"; +"common_enter_your_pin" = "پینتان را وارد کنید"; +"common_error" = "خطا"; +"common_everyone" = "هرکسی"; +"common_face_id_ios" = "شناسهٔ صورت"; +"common_failed" = "شکست خورد"; +"common_favourite" = "برگزیده"; +"common_favourited" = "برگزیده"; +"common_file" = "پرونده"; +"common_forward_message" = "هدایت پیام"; +"common_gif" = "جیف"; +"common_image" = "تصویر"; +"common_in_reply_to" = "در پاسخ به %1$@"; +"common_invite_unknown_profile" = "شناسهٔ ماتریکس نتوانست پیدا شود. ممکن است دعوت نرسیده باشد."; +"common_leaving_room" = "ترک کردن اتاق"; +"common_light" = "روشن"; +"common_link_copied_to_clipboard" = "پیوند در تخته‌گیره رونوشت شد"; +"common_loading" = "بار کردن…"; +"common_message" = "پیام"; +"common_message_actions" = "کنش‌های پیام"; +"common_message_layout" = "جینش پیام"; +"common_message_removed" = "پیام برداشته شد"; +"common_modern" = "نوین"; +"common_mute" = "بی‌صدا"; +"common_no_results" = "بدون نتیجه"; +"common_no_room_name" = "بدون نام اتاق"; +"common_offline" = "برون‌خط"; +"common_optic_id_ios" = "شناسهٔ نوری"; +"common_or" = "یا"; +"common_password" = "گذرواژه"; +"common_people" = "افراد"; +"common_permalink" = "پایاپیوند"; +"common_permission" = "اجازه"; +"common_please_wait" = "لطفا صبر کنید…"; +"common_poll_end_confirmation" = "مطئنید که می‌خواهید این نظرسنجی را پایان دهید؟"; +"common_poll_summary" = "نظرسنجی: %1$@"; +"common_poll_total_votes" = "مجموع آرا: %1$@"; +"common_poll_undisclosed_text" = "نتیجه‌ها پس از پایان نظرسنجی نشان داده خواهند شد"; +"common_privacy_policy" = "سیاست محرمانگی"; +"common_reaction" = "واکنش"; +"common_reactions" = "واکنش‌ها"; +"common_recovery_key" = "کلید بازیابی"; +"common_refreshing" = "تازه سازی…"; +"common_replying_to" = "پاسخ دادن به %1$@"; +"common_report_a_bug" = "گزارش یک اشکال"; +"common_report_a_problem" = "گزارش مشکل"; +"common_report_submitted" = "گزارش ثبت شد"; +"common_rich_text_editor" = "ویرایشگر متن غنی"; +"common_room" = "اتاق"; +"common_room_name" = "نام اتاق"; +"common_room_name_placeholder" = "برای نمونه نام پروژه‌تان"; +"common_saved_changes" = "تغییرات ذخیره شده"; +"common_saving" = "ذخیره کردن"; +"common_screen_lock" = "قفل صفحه"; +"common_search_for_someone" = "جست‌وجوی افراد"; +"common_search_results" = "نتایج جست‌وجو"; +"common_security" = "امنیت"; +"common_seen_by" = "دیده شده به دست"; +"common_sending" = "فرستادن…"; +"common_sending_failed" = "فرستادن شکست خورد"; +"common_sent" = "فرستاده"; +"common_server_not_supported" = "کارساز پشتیبانی نمی‌شود"; +"common_server_url" = "نشانی کارساز"; +"common_settings" = "تنظیمات"; +"common_shared_location" = "مکان هم‌رسانده"; +"common_signing_out" = "خارج شدن"; +"common_something_went_wrong" = "چیزی اشتباه پیش رفت"; +"common_starting_chat" = "آغازیدن گپ…"; +"common_sticker" = "عکس برگردان"; +"common_success" = "موفّقیت"; +"common_suggestions" = "پیشنهادها"; +"common_syncing" = "هم‌گام ساختن"; +"common_system" = "سامانه"; +"common_text" = "متن"; +"common_third_party_notices" = "تذکّرهای سوم‌شخص"; +"common_thread" = "رشته"; +"common_topic" = "موضوع"; +"common_topic_placeholder" = "این اتاق دربارهٔ چیست؟"; +"common_touch_id_ios" = "شناسهٔ لمس"; +"common_unable_to_decrypt" = "ناتوان در رمزگشایی"; +"common_unable_to_decrypt_no_access" = "به این پیام دسترسی ندارید"; +"common_unable_to_invite_message" = "دعوت‌ها نتوانستند به کاربرانی برسند."; +"common_unable_to_invite_title" = "ناتوان در فرستادن دعوت(ها)"; +"common_unlock" = "قفل‌گشایی"; +"common_unmute" = "باصدا"; +"common_unsupported_event" = "رویداد پشتیبانی نشده"; +"common_username" = "نام کاربری"; +"common_verification_cancelled" = "تأیید لغو شد"; +"common_verification_complete" = "تأیید کامل شد"; +"common_verify_device" = "تأیید افزاره"; +"common_video" = "ویدیو"; +"common_voice_message" = "پیام صوتی"; +"common_waiting" = "در انتظار…"; +"common_waiting_for_decryption_key" = "در انتظار این پیام"; +"common.copied_to_clipboard" = "در تخته‌گیره رونوشت شد"; +"common.do_not_show_this_again" = "این مورد را دوباره نشان نده"; +"common.open_source_licenses" = "پروانه‌های نرم‌افزاری آزاد"; +"common.pinned" = "سنجاق شده"; +"common.send_to" = "فرستادن به"; +"common.you" = "شما"; +"confirm_recovery_key_banner_message" = "Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup."; +"confirm_recovery_key_banner_title" = "ورود کلید بازیابیتان"; +"crash_detection_dialog_content" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; +"crypto_identity_change_pin_violation" = "به نظر می‌رسد هویت %1$@ تغییر کرده. %2$@"; +"dialog_permission_camera" = "In order to let the application use the camera, please grant the permission in the system settings."; +"dialog_permission_generic" = "لطفاً در تنظیمات سامانه اجازه بدهید."; +"dialog_permission_location_description_ios" = "اعطای دسترسی در تنظیمات -> مکان."; +"dialog_permission_location_title_ios" = "%1$@ به مکانتان دسترسی ندارد."; +"dialog_permission_microphone" = "In order to let the application use the microphone, please grant the permission in the system settings."; +"dialog_permission_microphone_description_ios" = "Grant access so you can record and send messages with audio."; +"dialog_permission_microphone_title_ios" = "%1$@ needs permission to access your microphone."; +"dialog_permission_notification" = "In order to let the application display notifications, please grant the permission in the system settings."; +"dialog_title_confirmation" = "تایید"; +"dialog_title_warning" = "هشدار"; +"dialog_unsaved_changes_description_ios" = "تغییراتتان ذخیره نمی‌شوند"; +"dialog_unsaved_changes_title" = "ذخیرهٔ تغییرات؟"; +"emoji_picker_category_activity" = "فعّالیت‌ها"; +"emoji_picker_category_flags" = "پرچم‌ها"; +"emoji_picker_category_foods" = "غذا و نوشیدنی"; +"emoji_picker_category_nature" = "حیوانات و طبعیت"; +"emoji_picker_category_objects" = "اشیا"; +"emoji_picker_category_people" = "شکلک‌ها و افراد"; +"emoji_picker_category_places" = "سفر و مکان‌ها"; +"emoji_picker_category_symbols" = "نمادها"; +"error_account_creation_not_possible" = "Your homeserver needs to be upgraded to support Matrix Authentication Service and account creation."; +"error_failed_creating_the_permalink" = "شکست در ایجاد پایاپیوند"; +"error_failed_loading_map" = "%1$@ could not load the map. Please try again later."; +"error_failed_loading_messages" = "شکست در بار کردن پیام‌ها"; +"error_failed_locating_user" = "%1$@ could not access your location. Please try again later."; +"error_failed_uploading_voice_message" = "شکست در بارگذاری پیام صوتیتان."; +"error_message_not_found" = "پیام پیدا نشد"; +"error_no_compatible_app_found" = "No compatible app was found to handle this action."; +"error_some_messages_have_not_been_sent" = "برخی پیام‌ها ارسال نشده‌اند"; +"error_unknown" = "متأسفیم ، خطایی رخ داد"; +"event_shield_reason_authenticity_not_guaranteed" = "اعتبار این پیام رمز شده نمی‌تواند روی این افزاره تأیید شود."; +"event_shield_reason_previously_verified" = "رمز شده به دست کاربری از پیش تأیید شده."; +"event_shield_reason_sent_in_clear" = "رمز نشده."; +"event_shield_reason_unknown_device" = "رمز شده به دست افزاره‌ای ناشناخته یا حذف شده."; +"event_shield_reason_unsigned_device" = "رمز شده به دست افزاره‌ای که از سوی مالکش تأیید نشده."; +"event_shield_reason_unverified_identity" = "رمز شده به دست کاربری تأیید نشده."; +"full_screen_intent_banner_message" = "To ensure you never miss an important call, please change your settings to allow full-screen notifications when your phone is locked."; +"full_screen_intent_banner_title" = "بهبود تجریهٔ تماستان"; +"invite_friends_rich_title" = "🔐️ پییوستن به من روی %1$@"; +"invite_friends_text" = "درود. با من روی %1$@ صحبت کن: %2$@"; +"leave_conversation_alert_subtitle" = "Are you sure that you want to leave this conversation? This conversation is not public and you won't be able to rejoin without an invite."; +"leave_room_alert_empty_subtitle" = "Are you sure that you want to leave this room? You're the only person here. If you leave, no one will be able to join in the future, including you."; +"leave_room_alert_private_subtitle" = "Are you sure that you want to leave this room? This room is not public and you won't be able to rejoin without an invite."; +"leave_room_alert_subtitle" = "Are you sure that you want to leave the room?"; +"login_initial_device_name_ios" = "%1$@ آی‌اواس"; +"notification_channel_call" = "تماس"; +"notification_channel_listening_for_events" = "در حال گوش دادن به رویدادها"; +"notification_channel_noisy" = "اعلان‌های پرصدا"; +"notification_channel_ringing_calls" = "زنگ خوردن تماس"; +"notification_channel_silent" = "اعلان‌های صامت"; +"notification_incoming_call" = "تماس ورودی"; +"notification_inline_reply_failed" = "**‌شکست در فرستادن - لطفاً اتاق را بگشایید"; +"notification_invite_body" = "به گپ دعوتتان کرد"; +"notification_invite_body_with_sender" = "%1$@ به گپ دعوتتان کرد"; +"notification_mentioned_you_body" = "به شما اشاره کرد: %1$@"; +"notification_new_messages" = "پیام جدید"; +"notification_reaction_body" = "با %1$@ واکنش داد"; +"notification_room_invite_body" = "دعوت کرد به اتاق بپیوندید"; +"notification_room_invite_body_with_sender" = "%1$@ دعوت کرد به اتاق بپیوندید"; +"notification_sender_me" = "خودم"; +"notification_sender_mention_reply" = "%1$@ اشاره کرد یا پاسخ داد"; +"notification_test_push_notification_content" = "دارید آگاهی را مشاهده می‌کنید! کلیک کنید!"; +"notification_ticker_text_dm" = "%1$@: %2$@"; +"notification_ticker_text_group" = "%1$@: %2$@ %3$@"; +"notification_unread_notified_messages_and_invitation" = "%1$@ و %2$@"; +"notification_unread_notified_messages_in_room" = "%1$@ در %2$@"; +"notification_unread_notified_messages_in_room_and_invitation" = "%1$@ در %2$@ و %3$@"; +"preference_rageshake" = "Rageshake to report bug"; +"rageshake_detection_dialog_content" = "به نظر می‌رسد دارید گوشی خود را به دلیل کار نکردن تکان می‌دهید! آیا می‌خواهید یک اشکال در برنامه گزارش نمایید؟"; +"rich_text_editor_bullet_list" = "تغییر وضعیت سیاههٔ گلوله‌ای"; +"rich_text_editor_close_formatting_options" = "بستن گزینه‌های قالب‌بندی"; +"rich_text_editor_code_block" = "تغییر حالت بلوک کد"; +"rich_text_editor_composer_placeholder" = "پیام…"; +"rich_text_editor_create_link" = "ایجاد پیوند"; +"rich_text_editor_edit_link" = "ویرایش پیوند"; +"rich_text_editor_format_bold" = "اعمال قالب توپر"; +"rich_text_editor_format_italic" = "اعمال قالب کج"; +"rich_text_editor_format_strikethrough" = "اعمال قالب خط‌خورده"; +"rich_text_editor_format_underline" = "اعمال قالب زیرخط‌دار"; +"rich_text_editor_full_screen_toggle" = "تغییر حالت تمام‌صفحه"; +"rich_text_editor_indent" = "تورفتگی"; +"rich_text_editor_inline_code" = "اعمال قالب کد درون‌خط"; +"rich_text_editor_link" = "تنظیم پیوند"; +"rich_text_editor_numbered_list" = "تغییر وضعیت سیاههٔ شماره‌دار"; +"rich_text_editor_open_compose_options" = "گشودن گزینه‌های نوشتن"; +"rich_text_editor_quote" = "تغییر حالت نقل قول"; +"rich_text_editor_remove_link" = "برداشتن پیوند"; +"rich_text_editor_unindent" = "تونرفتگی"; +"rich_text_editor_url_placeholder" = "پیوند"; +"rich_text_editor_a11y_add_attachment" = "افزودن پیوست"; +"screen_advanced_settings_element_call_base_url" = "نشانی پایهٔ تماس المنتی سفارشی"; +"screen_advanced_settings_element_call_base_url_description" = "تنظمی نشانی پایه‌‌ای سفارشی برای تماس المنتی."; +"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_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; +"screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; +"screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; +"screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; +"screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; +"screen_resolve_send_failure_changed_identity_title" = "Your message was not sent because %1$@’s verified identity has changed"; +"screen_resolve_send_failure_unsigned_device_primary_button_title" = "فرستادن پیام به هر روی"; +"screen_resolve_send_failure_unsigned_device_subtitle" = "%1$@ is using one or more unverified devices. You can send the message anyway, or you can cancel for now and try again later after %2$@ has verified all their devices."; +"screen_resolve_send_failure_unsigned_device_title" = "Your message was not sent because %1$@ has not verified all devices"; +"screen_resolve_send_failure_you_unsigned_device_subtitle" = "One or more of your devices are unverified. You can send the message anyway, or you can cancel for now and try again later after you have verified all of your devices."; +"screen_resolve_send_failure_you_unsigned_device_title" = "Your message was not sent because you have not verified one or more of your devices"; +"screen_room_mentions_at_room_subtitle" = "آگاهی به تمام اتاق"; +"screen_room_pinned_banner_indicator" = "%1$@ از %2$@"; +"screen_room_pinned_banner_indicator_description" = "%1$@ پیام‌های سنجاق شده"; +"screen_room_pinned_banner_loading_description" = "بار کردن پشام‌ها…"; +"screen_room_pinned_banner_view_all_button_title" = "نمایش همه"; +"screen_room_details_pinned_events_row_title" = "پیام‌های سنجاق شده"; +"screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; +"screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; +"screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; +"screen_account_provider_form_hint" = "نشانی کارساز خانگی"; +"screen_account_provider_form_notice" = "ورود عبارت جست‌وجو یا نشانی دامنه."; +"screen_account_provider_form_subtitle" = "جست‌وجو برای شرکت، اجتماع یا کارسازی خصوصی."; +"screen_account_provider_form_title" = "یافتن فراهم کنندهٔ حساب"; +"screen_account_provider_signin_title" = "دارید وارد %@ می‌شوید"; +"screen_account_provider_signup_title" = "دارید حسابی روی %@ می‌سازید"; +"screen_advanced_settings_developer_mode" = "حالت توسعه‌دهنده"; +"screen_advanced_settings_developer_mode_description" = "Enable to have access to features and functionality for developers."; +"screen_advanced_settings_rich_text_editor_description" = "از کار انداختن ویرایشگر متن غنی یا نوشتن دستی مارک‌دون."; +"screen_advanced_settings_send_read_receipts" = "رسید‌های خواندن"; +"screen_advanced_settings_send_read_receipts_description" = "If turned off, your read receipts won't be sent to anyone. You will still receive read receipts from other users."; +"screen_advanced_settings_share_presence" = "هم‌رسانی حضور"; +"screen_advanced_settings_share_presence_description" = "If turned off, you won’t be able to send or receive read receipts or typing notifications."; +"screen_advanced_settings_view_source_description" = "Enable option to view message source in the timeline."; +"screen_analytics_prompt_data_usage" = "We won't record or profile any personal data"; +"screen_analytics_prompt_help_us_improve" = "Share anonymous usage data to help us identify issues."; +"screen_analytics_prompt_read_terms" = "You can read all our terms %1$@."; +"screen_analytics_prompt_read_terms_content_link" = "این‌جا"; +"screen_analytics_prompt_settings" = "می‌توانید در هر زمان خاموشش کنید"; +"screen_analytics_prompt_third_party_sharing" = "داده‌هایتان را با سوم‌شخص‌ها هم‌نمی‌رسانیم"; +"screen_analytics_prompt_title" = "کمک به بهبود %1$@"; +"screen_analytics_settings_share_data" = "هم رسانی داده‌های تحلیلی"; +"screen_app_lock_biometric_authentication" = "هویت‌سنجی زیستی"; +"screen_app_lock_biometric_unlock" = "قفل‌گشایی زیست‌سنجی"; +"screen_app_lock_biometric_unlock_reason_ios" = "برای دسترسی به کاره‌تان نیاز به هویت‌سنجی است"; +"screen_app_lock_forgot_pin" = "فراموشی پین؟"; +"screen_app_lock_settings_change_pin" = "تغییر کد پین"; +"screen_app_lock_settings_enable_biometric_unlock" = "احازه به قفل گشایی زیست‌سنجی"; +"screen_app_lock_settings_enable_face_id_ios" = "اجازه به شناسهٔ صورت"; +"screen_app_lock_settings_enable_optic_id_ios" = "اجازه به شناسهٔ نوری"; +"screen_app_lock_settings_enable_touch_id_ios" = "اجازه به شناسهٔ لمسی"; +"screen_app_lock_settings_remove_pin" = "برداشتن پین"; +"screen_app_lock_settings_remove_pin_alert_message" = "مطمئنید که می‌خواهید پین را بردارید؟"; +"screen_app_lock_settings_remove_pin_alert_title" = "برداشتن پین؟"; +"screen_app_lock_setup_biometric_unlock_allow_title" = "اجازه به %1$@"; +"screen_app_lock_setup_biometric_unlock_skip" = "ترجیح می‌دهم از پین استفاده کنم"; +"screen_app_lock_setup_biometric_unlock_subtitle" = "زمیانتان را ذخیره کرده و از %1$@ برای قفل‌گشایی هربارهٔ کاره استفاده کنید"; +"screen_app_lock_setup_choose_pin" = "گزینش پین"; +"screen_app_lock_setup_confirm_pin" = "تأیید پین"; +"screen_app_lock_setup_pin_context" = "Lock %1$@ to add extra security to your chats.\n\nChoose something memorable. If you forget this PIN, you will be logged out of the app."; +"screen_app_lock_setup_pin_forbidden_dialog_content" = "به دلیل امنیتی نمی‌توانید این پین را برگزینید"; +"screen_app_lock_setup_pin_forbidden_dialog_title" = "گزینشی پینی متفاوت"; +"screen_app_lock_setup_pin_mismatch_dialog_content" = "لطفاً یک پین را دو بار وارد کنید"; +"screen_app_lock_setup_pin_mismatch_dialog_title" = "پین‌ها مطابق نیستند"; +"screen_app_lock_signout_alert_message" = "برای ادامه باید دوباره وارد شده و پینی جدید ایجاد کنید"; +"screen_app_lock_signout_alert_title" = "دارید خارج می‌شوید"; +"screen_blocked_users_empty" = "هیچ کاربر مسدودی ندارید"; +"screen_blocked_users_unblocking" = "رفع کردن انسداد…"; +"screen_bug_report_attach_screenshot" = "پیوست نماگرفت"; +"screen_bug_report_contact_me" = "اگر پرسش دیگری دارید، می‌توانید با من در تماس باشید."; +"screen_bug_report_contact_me_title" = "تماس با من"; +"screen_bug_report_edit_screenshot" = "ویرایش نماگرفت"; +"screen_bug_report_editor_description" = "Please describe the problem. What did you do? What did you expect to happen? What actually happened. Please go into as much detail as you can."; +"screen_bug_report_editor_placeholder" = "شرح مشکل…"; +"screen_bug_report_editor_supporting" = "ترجیحاً توضیحات را به زبان انگلیسی بنویسید."; +"screen_bug_report_error_description_too_short" = "The description is too short, please provide more details about what happened. Thanks!"; +"screen_bug_report_include_crash_logs" = "ارسال رخدادنگارهای خطا"; +"screen_bug_report_include_logs" = "اجازه به گزارش‌ها"; +"screen_bug_report_include_screenshot" = "ارسال تصویر صفحه"; +"screen_bug_report_logs_description" = "Logs will be included with your message to make sure that everything is working properly. To send your message without logs, turn off this setting."; +"screen_bug_report_view_logs" = "دیدن گزارش‌ها"; +"screen_change_account_provider_matrix_org_subtitle" = "ماتریکس‌دات‌اورگ کارسازی بزرگ و آزاد روی شبکهٔ ماتریکس عمومی برای ارتباطات نامتمرکز و امن است که به دست بنیاد ماتریکس‌دات‌اورگ اداره می‌شود."; +"screen_change_account_provider_other" = "دیگر"; +"screen_change_account_provider_subtitle" = "استفاده از فراهم کنندهٔ حسابی دیگر چون کارساز خصوصی خوتان یا حسابی کاری."; +"screen_change_account_provider_title" = "تغییر فراهم کنندهٔ حساب"; +"screen_change_server_error_invalid_homeserver" = "We couldn't reach this homeserver. Please check that you have entered the homeserver URL correctly. If the URL is correct, contact your homeserver administrator for further help."; +"screen_change_server_error_invalid_well_known" = "Sliding sync isn't available due to an issue in the well-known file:\n%1$@"; +"screen_change_server_error_no_sliding_sync_message" = "این کارساز در حال حاضر از هم‌گام سازی اسلایدی پشتیبانی نمی‌کند."; +"screen_change_server_form_header" = "نشانی کارساز خانگی"; +"screen_change_server_form_notice" = "تنها می‌توانید به کارسازهای موجودی که از هم‌گام سازی اسلاید پشتیبانی می‌کنند وصل شود. مدیر کارساز خانگیتان باید پیکربندیش کند. %1$@"; +"screen_change_server_subtitle" = "نشانی کارسازتان چیست؟"; +"screen_change_server_title" = "کارسازتان را برگزینید"; +"screen_chat_backup_key_backup_action_disable" = "خاموش کردن پشتیبان"; +"screen_chat_backup_key_backup_action_enable" = "روشن کردن پشتیبان"; +"screen_chat_backup_key_backup_description" = "پشتیبان‌ها اطمینان می‌دهند که تاریخچهٔ پیام‌هایتان را از دست نمی‌دهید. %1$@."; +"screen_chat_backup_key_backup_title" = "پشتیبان گیری"; +"screen_chat_backup_recovery_action_change" = "تغییر کلید بازیابی"; +"screen_chat_backup_recovery_action_confirm_description" = "پشتیبان گپتان از هم‌گام بودن در آمده."; +"screen_chat_backup_recovery_action_setup" = "برپایی بازیابی"; +"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."; +"screen_create_account_title" = "ایجاد حساب"; +"screen_create_new_recovery_key_list_item_1" = "گشودن %1$@ در افزارهٔ میزکار"; +"screen_create_new_recovery_key_list_item_2" = "ورود دوباره به حسابتان"; +"screen_create_new_recovery_key_list_item_3" = "گزینش %1$@ هنگام درخواست تأیید افزاره‌تان"; +"screen_create_new_recovery_key_list_item_3_reset_all" = "«بازنشانی همه»"; +"screen_create_new_recovery_key_list_item_4" = "پیروی از دستورالعمل‌ها برای ایجاد کلید بازیابی جدید"; +"screen_create_new_recovery_key_list_item_5" = "ذخیرهٔ کلید بازیابی جدیدتان در مدیر گذرواژه یا یادداشت رمز شده"; +"screen_create_new_recovery_key_title" = "بازنشانی رمزنگاری برای حسابتان با استفاده از افزاره‌ای دیگر"; +"screen_create_poll_add_option_btn" = "افزودن گزینه"; +"screen_create_poll_anonymous_desc" = "نمایش نتیجه‌ها تنها پس از پایان نظرسنجی"; +"screen_create_poll_anonymous_headline" = "نهفتن رأی‌ها"; +"screen_create_poll_answer_hint" = "گزینهٔ %1$d"; +"screen_create_poll_cancel_confirmation_title_ios" = "لغو نظرسنجی"; +"screen_create_poll_question_desc" = "پرسش یا موضوع"; +"screen_create_poll_question_hint" = "این نظرسنجی دربارهٔ چیست؟"; +"screen_create_poll_title" = "ایجاد نظرسنجی"; +"screen_create_room_action_create_room" = "اتاق جدید"; +"screen_create_room_error_creating_room" = "هنگام ایجاد اتاق خطایی رخ داد"; +"screen_create_room_private_option_description" = "پیام‌های این اتاق رمز شده‌اند. رمزنگاری نمی‌تواند از این پس تغییر کند."; +"screen_create_room_private_option_title" = "اتاق خصوصی (فقط دعوت)"; +"screen_create_room_public_option_description" = "پیام‌ها رمزنگاری نشده و هرکسی می‌تواند بخواندشان. می‌توانید بعداً رمزنگاری را به کار بیندازید."; +"screen_create_room_public_option_title" = "اتاق عمومی (هرکسی)"; +"screen_create_room_topic_label" = "موضوع (اختیاری)"; +"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" = "حذف همهٔ پیام‌هایم"; +"screen_deactivate_account_delete_all_messages_notice" = "Warning: Future users may see incomplete conversations."; +"screen_deactivate_account_description" = "Deactivating your account is %1$@, it will:"; +"screen_deactivate_account_description_bold_part" = "بازگشت‌ناپذیر"; +"screen_deactivate_account_list_item_1" = "%1$@ your account (you can't log back in, and your ID can't be reused)."; +"screen_deactivate_account_list_item_1_bold_part" = "از کار انداختن دایمی"; +"screen_deactivate_account_list_item_2" = "برداشتنتان از همهٔ اتاق‌های گپ."; +"screen_deactivate_account_list_item_3" = "حذف اطّلاعات حسابتان از کارساز هویت."; +"screen_deactivate_account_list_item_4" = "Your messages will still be visible to registered users but won’t be available to new or unregistered users if you choose to delete them."; +"screen_deactivate_account_title" = "غیرفعّال‌سازی حساب"; +"screen_edit_poll_delete_confirmation" = "مطمئنید که می‌خواهید این نظرسنجی را حذف کنید؟"; +"screen_edit_profile_display_name" = "نام نمایشی"; +"screen_edit_profile_display_name_placeholder" = "نام نمایشیتان"; +"screen_edit_profile_error" = "خطایی ناشناخته رخ داد و اطّلاعات نتوانستند تغییر کنند."; +"screen_edit_profile_error_title" = "ناتوان در به‌روز کردن نمایه"; +"screen_edit_profile_title" = "ویرایش نمایه"; +"screen_edit_profile_updating_details" = "به‌روز کردن نمایه…"; +"screen_encryption_reset_action_continue_reset" = "ادامهٔ بازنشانی"; +"screen_encryption_reset_bullet_1" = "جزییات حساب، آشنایان، ترجیحات و سیاههٔ گپ‌هایتان حفظ خواهند شد"; +"screen_encryption_reset_bullet_2" = "تاریخچهٔ گپ‌هایتان را از دست خواهید داد"; +"screen_encryption_reset_bullet_3" = "لازم است دوباره همهٔ آشنایان و افزاره‌های موجودتان را تأیید کنید"; +"screen_encryption_reset_footer" = "فقط اگر به افزاره‌ای وارد شده از پیش دسترسی ندارید و کلید بازیابیتان را گم کرده‌اید بازنشانی کنید."; +"screen_encryption_reset_title" = "نمی‌توانید تأیید کنید؟ لازم است هویتتان را بازنشانی کنید."; +"screen_identity_confirmation_cannot_confirm" = "نمی‌توانید تأیید کنید؟"; +"screen_identity_confirmation_create_new_recovery_key" = "ایجاد کلید بازیابی جدید"; +"screen_identity_confirmation_subtitle" = "تأیید این افزاره برای برپایی پیام‌رسانی امن."; +"screen_identity_confirmation_title" = "تأیید هویتتان"; +"screen_identity_confirmation_use_another_device" = "استفاده از افزاره‌ای دیگر"; +"screen_identity_confirmation_use_recovery_key" = "استفاده از کلید بازیابی"; +"screen_identity_confirmed_subtitle" = "اکنون می‌توانید پیام‌ها را به صورت امن فرستاده و بگیرید و هرکسی که با او گپ می‌زنید نیز می‌تواند به این افزاره اعتماد کند."; +"screen_identity_confirmed_title" = "افزاره تأیید شده"; +"screen_identity_waiting_on_other_device" = "منتظر افزارهٔ دیگر…"; +"screen_invites_decline_chat_message" = "مطمئنید که می‌خواهید دعوت پیوستن به %1$@ را رد کنید؟"; +"screen_invites_decline_chat_title" = "رد دعوت"; +"screen_invites_decline_direct_chat_message" = "مطمئنید که می‌خواهید این گپ خصوصی با %1$@ را رد کنید؟"; +"screen_invites_decline_direct_chat_title" = "رد گپ"; +"screen_invites_empty_list" = "بدون دعوت"; +"screen_invites_invited_you" = "%1$@ (%2$@) دعوتتان کرد"; +"screen_join_room_join_action" = "پیوستن به اتاق"; +"screen_join_room_knock_action" = "در زدن برای پیوستن"; +"screen_join_room_space_not_supported_description" = "%1$@ هنوز از فضاها پشتیبانی نمی‌کند. می‌توانید روی وب به فضاها دسترسی داشته باشید."; +"screen_join_room_space_not_supported_title" = "فضاها هنوز پشتیبانی نمی‌شوند"; +"screen_join_room_subtitle_knock" = "زدن روی این دکمه برای آگاه شدن مدیر اتاق. پس از تأیید می‌توانید به گفت‌وگو بپیوندید."; +"screen_join_room_subtitle_no_preview" = "برای دیدن تاریخچهٔ پیام باید عضو این اتاق باشید."; +"screen_join_room_title_knock" = "می‌خواهید به اتاق بپیوندید؟"; +"screen_join_room_title_no_preview" = "پیش‌نمایش موجود نیست"; +"screen_key_backup_disable_confirmation_action_turn_off" = "خاموش کردن"; +"screen_key_backup_disable_confirmation_description" = "You will lose your encrypted messages if you are signed out of all devices."; +"screen_key_backup_disable_confirmation_title" = "Are you sure you want to turn off backup?"; +"screen_key_backup_disable_description" = "Turning off backup will remove your current encryption key backup and turn off other security features. In this case, you will:"; +"screen_key_backup_disable_description_point_1" = "Not have encrypted message history on new devices"; +"screen_key_backup_disable_description_point_2" = "Lose access to your encrypted messages if you are signed out of %1$@ everywhere"; +"screen_key_backup_disable_title" = "Are you sure you want to turn off backup?"; +"screen_login_error_deactivated_account" = "این حساب از کار افتاده است."; +"screen_login_error_invalid_credentials" = "نام کاربری یا گذرواژه نامعتبر است"; +"screen_login_error_invalid_user_id" = "این یک شناسه کاربری معتبر نیست. قالب صحیح: ‪«@user:homeserver.or"; +"screen_login_error_refresh_tokens" = "This server is configured to use refresh tokens. These aren't supported when using password based login."; +"screen_login_error_unsupported_authentication" = "The selected homeserver doesn't support password or OIDC login. Please contact your admin or choose another homeserver."; +"screen_login_form_header" = "جزییاتتان را وارد کنید"; +"screen_login_title" = "خوش برگشتید!"; +"screen_login_title_with_homeserver" = "ورود به %1$@"; +"screen_media_picker_error_failed_selection" = "گزینش رسانه شکست خورد. لطفاً دوباره تلاش کنید."; +"screen_media_upload_preview_error_failed_processing" = "پردازش رسانه برای بارگذاری شکست خورد. لطفاً دوباره تلاش کنید."; +"screen_media_upload_preview_error_failed_sending" = "بارگذاری رسانه شکست خورد. لطفاً دوباره تلاش کنید."; +"screen_migration_message" = "فرایندی یک باره است. ممنون از شکیباییتان."; +"screen_migration_title" = "برپایی حسابتان."; +"screen_notification_optin_subtitle" = "می‌توانید بعداً تنظیماتتان را تغییر دهید."; +"screen_notification_optin_title" = "اجازه به آگاهی‌ها و از دست ندادن پیام‌ها"; +"screen_notification_settings_additional_settings_section_title" = "تنظیمات اضافی"; +"screen_notification_settings_calls_label" = "تماس‌های صوتی و تصویری"; +"screen_notification_settings_configuration_mismatch" = "نامتطابقت در پیکربندی"; +"screen_notification_settings_configuration_mismatch_description" = "تنظمیات آگاهی را ساده کرده‌ایم تا یافتن انتخاب‌ها را ساده‌تر کنیم. برهی تنظمیات سفارسی که در گذشته گزیده‌اید این‌جا نشان داده نمی‌شوند؛ ولی همچنن فعّالند.\n\nبا ادامه داد ممکن است برخی تنظیماتتان تغییر کنند."; +"screen_notification_settings_direct_chats" = "گپ‌های مستقیم"; +"screen_notification_settings_edit_custom_settings_section_title" = "تنظیمات سفارشی برای هر گپ"; +"screen_notification_settings_edit_failed_updating_default_mode" = "هنگام به‌روز کردن تنظیمات آگاهی خطایی رخ داد."; +"screen_notification_settings_edit_mode_all_messages" = "همهٔ پیام‌ها"; +"screen_notification_settings_edit_mode_mentions_and_keywords" = "فقط اشاره‌ها و کلیدواژگان"; +"screen_notification_settings_edit_screen_direct_section_header" = "آگاهی در گپ‌های مستقیم برای"; +"screen_notification_settings_edit_screen_group_section_header" = "آگاهی در گپ‌های گروهی برای"; +"screen_notification_settings_enable_notifications" = "به کار انداختن آگاهی‌ها روی این افزاره"; +"screen_notification_settings_failed_fixing_configuration" = "پیکربندی درست نشد. لطفاً دوباره تلاش کنید."; +"screen_notification_settings_group_chats" = "گپ‌های گروهی"; +"screen_notification_settings_invite_for_me_label" = "دعوت‌ها"; +"screen_notification_settings_mentions_only_disclaimer" = "کارساز خانگیتان از این گزینه در اتاق‌های رمز شده پشتیبانی نمی‌کند. ممکن است در برخی اتاق‌ها آگاه نشوید."; +"screen_notification_settings_mode_all" = "همه"; +"screen_notification_settings_mode_mentions" = "اشاره‌ها"; +"screen_notification_settings_notification_section_title" = "آگاه کردنم برای"; +"screen_notification_settings_room_mention_label" = "آگاه کردنم برای ‪@room"; +"screen_notification_settings_system_notifications_action_required" = "برای گرفتن آگاهی‌ها لطفاً%1$@تان را تغییر دهید."; +"screen_notification_settings_system_notifications_action_required_content_link" = "تنظیمات سامانه"; +"screen_notification_settings_system_notifications_turned_off" = "آگاهی‌های سامانه‌ای خاموش شدند"; +"screen_notification_settings_title" = "آگاهی‌ها"; +"screen_onboarding_sign_in_manually" = "ورود دستی"; +"screen_onboarding_sign_in_with_qr_code" = "ورود با کد QR"; +"screen_onboarding_sign_up" = "ایجاد حساب"; +"screen_onboarding_welcome_message" = "به سریع‌ترین %1$@ خوش آمدید. بازطرّاحی شده برای سرعت و سادگی."; +"screen_onboarding_welcome_subtitle" = "به %1$@ خوش آمدید. بازطرّاحی شده برای سرعت و سادگی."; +"screen_onboarding_welcome_title" = "در المنتتان باشید"; +"screen_polls_history_empty_ongoing" = "نتوانست هیچ نظرسنجی در جریانی بیابد."; +"screen_polls_history_empty_past" = "نتوانست هیچ نظرسنجی گذشته‌ای بیابد."; +"screen_polls_history_filter_ongoing" = "در جریان"; +"screen_polls_history_filter_past" = "گذشته"; +"screen_polls_history_title" = "نظرسنجی‌ها"; +"screen_qr_code_login_connecting_subtitle" = "برقرار کدن اتّصالی امن"; +"screen_qr_code_login_connection_note_secure_state_description" = "نتوانست اتّصالی امن به افزارهٔ جدید بسازد. افزاره‌های موجودتان هنوز امنند و نیازی نیست نگرانشان باشید."; +"screen_qr_code_login_connection_note_secure_state_list_header" = "اکنون چه؟"; +"screen_qr_code_login_connection_note_secure_state_list_item_1" = "Try signing in again with a QR code in case this was a network problem"; +"screen_qr_code_login_connection_note_secure_state_list_item_2" = "If you encounter the same problem, try a different wifi network or use your mobile data instead of wifi"; +"screen_qr_code_login_connection_note_secure_state_list_item_3" = "ورود دستی در صورت کار نکردنش"; +"screen_qr_code_login_connection_note_secure_state_title" = "اتّصال ناامن"; +"screen_qr_code_login_device_code_subtitle" = "از شما خواسته خواهد شد که دو رقم نشان داده روی این افزاره را وارد کنید."; +"screen_qr_code_login_device_code_title" = "شمارهٔ زیر را روی افزارهٔ دیگرتان وارد کنید"; +"screen_qr_code_login_device_not_signed_in_scan_state_description" = "به افزارهٔ دیگرتان وارد شده و دوباره تلاش کنید یا از افزارهٔ دیگری که از پیش وارد شده استفاده کنید."; +"screen_qr_code_login_device_not_signed_in_scan_state_subtitle" = "افزارهٔ دیگر وارد نشده"; +"screen_qr_code_login_error_cancelled_subtitle" = "ورود روی افزارهٔ دیگر لغو شد."; +"screen_qr_code_login_error_cancelled_title" = "درخواست ورد لغو شد"; +"screen_qr_code_login_error_declined_subtitle" = "ورود به دست افزارهٔ دیگر رد شد."; +"screen_qr_code_login_error_declined_title" = "ورود رد شد"; +"screen_qr_code_login_error_expired_subtitle" = "ورود منقضی شد. لطفاً دوباره تلاش کنید."; +"screen_qr_code_login_error_expired_title" = "ورود در زمان معیّن کامل نشد"; +"screen_qr_code_login_error_linking_not_suported_subtitle" = "افزارهٔ دیگرتان از ورود به %@ با کد پاس پشتیبانی نمی‌کند.\n\nآزمودن ورود دستی یا پویش کد پاس با افزاره‌ای دیگر."; +"screen_qr_code_login_error_linking_not_suported_title" = "کد پاس پشتیبانی نمی‌شود"; +"screen_qr_code_login_error_sliding_sync_not_supported_subtitle" = "فراهم کنندهٔ حسابتان از %1$@ پشتیبانی نمی‌کند."; +"screen_qr_code_login_error_sliding_sync_not_supported_title" = "%1$@ پشتیبانی نمی‌شود"; +"screen_qr_code_login_initial_state_button_title" = "آمادهٔ پویش"; +"screen_qr_code_login_initial_state_item_1" = "گشودن %1$@ در افزارهٔ میزکار"; +"screen_qr_code_login_initial_state_item_2" = "زدن روی چهرکتان"; +"screen_qr_code_login_initial_state_item_3" = "گزینش %1$@"; +"screen_qr_code_login_initial_state_item_3_action" = "«پیوند افزارهٔ جدید»"; +"screen_qr_code_login_initial_state_item_4" = "پویش کد پاس با این افزاره"; +"screen_qr_code_login_initial_state_title" = "گشودن %1$@ روی افزاره‌ای دیگر برای گرفتن کد پاس"; +"screen_qr_code_login_invalid_scan_state_description" = "استفاده از کد پاس نشان داده روی افزارهٔ دیگر."; +"screen_qr_code_login_invalid_scan_state_subtitle" = "کد پاس اشتباه"; +"screen_qr_code_login_no_camera_permission_button" = "رفتن به تنظیمات دوربین"; +"screen_qr_code_login_no_camera_permission_state_description" = "برای ادامه باید اجازهٔ استفادهٔ %1$@ از دوربین افزاره‌تان را بدهید."; +"screen_qr_code_login_no_camera_permission_state_title" = "اجازهٔ دسترسی دوربین برای پویش کد پاس"; +"screen_qr_code_login_scanning_state_title" = "پویش کد پاس"; +"screen_qr_code_login_start_over_button" = "آغاز از نو"; +"screen_qr_code_login_unknown_error_description" = "خطایی غیرمنتظره رخ داد. لطفاً دوباره تلاش کنید."; +"screen_qr_code_login_verify_code_loading" = "منتظر افزارهٔ دیگرتان"; +"screen_qr_code_login_verify_code_subtitle" = "ممکن است فراهم کنندهٔ حسابتان کد زیر را برای تأیید ورود بخواهد."; +"screen_qr_code_login_verify_code_title" = "کد تأییدتان"; +"screen_recovery_key_change_description" = "گرفتن کلید بازیابی جدید در صورت فراموشی کلید کنونی. پس از تغییر دادن کلید بازیابیتان، کلید پیشین دیگر کار نخواهد کرد."; +"screen_recovery_key_change_generate_key" = "تولید کلید بازیابی جدید"; +"screen_recovery_key_change_generate_key_description" = "اطمینان از امکان نگه داری کلید بازیابیتان در جایی امن"; +"screen_recovery_key_change_success" = "کلید بازیابی تغییر کرد"; +"screen_recovery_key_change_title" = "تغییر کلید بازیابی؟"; +"screen_recovery_key_confirm_create_new_recovery_key" = "ایجاد کلید بازیابی جدید"; +"screen_recovery_key_confirm_description" = "اطمینان از این که کسی نمی‌تواند این صفحه را ببیند!"; +"screen_recovery_key_confirm_error_content" = "لطفاً برای تأیید دسترسی به پشتیبان گپتان دوباره تلاش کنید."; +"screen_recovery_key_confirm_error_title" = "کلید بازیابی اشتباه"; +"screen_recovery_key_confirm_key_description" = "کلید امنیتی یا عبارت امنیتی نیز باید کار کنند."; +"screen_recovery_key_confirm_key_placeholder" = "ورود…"; +"screen_recovery_key_confirm_lost_recovery_key" = "گم کردن کلید بازیابیتان؟"; +"screen_recovery_key_confirm_success" = "کلید بازیابی تأیید شد"; +"screen_recovery_key_copied_to_clipboard" = "کلید بازیابی رونوشت شد"; +"screen_recovery_key_generating_key" = "تولید کردن…"; +"screen_recovery_key_save_action" = "ذخیرهٔ کلید بازیابی"; +"screen_recovery_key_save_description" = "نوشتن کلید بازیابیتان در جایی امن یا ذخیره‌اش در مدیر گذرواژه."; +"screen_recovery_key_save_key_description" = "زدن برای رونوشت از کلید بازیابی"; +"screen_recovery_key_save_title" = "ذخیرهٔ کلید بازیابیتان"; +"screen_recovery_key_setup_confirmation_description" = "پس از این برپایی قادر به دسترسی به کلید بازیابی جدیدتان نخواهید بود."; +"screen_recovery_key_setup_confirmation_title" = "کلید بازیابیتان را ذخیره کرده‌اید؟"; +"screen_recovery_key_setup_description" = "پشتیبان گپتان با کلید بازیابی محافظت می‌شود. اگر پس از برپایی نیاز به کلید بازیابی جدیدی داشتید می‌توانید با گزینش «دگرگونی کلید بازیابی» دوباره ایجادش کنید."; +"screen_recovery_key_setup_generate_key" = "تولید کلید بازیابیتان"; +"screen_recovery_key_setup_generate_key_description" = "اطمینان از امکان نگه داری کلید بازیابیتان در جایی امن"; +"screen_recovery_key_setup_success" = "برپایی بازیابی موفّق بود"; +"screen_recovery_key_setup_title" = "برپایی بازیابی"; +"screen_report_content_block_user_hint" = "Check if you want to hide all current and future messages from this user"; +"screen_report_content_explanation" = "This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages."; +"screen_report_content_hint" = "دلیل گزارش این محتوا"; +"screen_reset_encryption_confirmation_alert_action" = "بله. اکنون بازنشانی شود"; +"screen_reset_encryption_confirmation_alert_subtitle" = "این فرایند بازگشت‌ناپذیر است."; +"screen_reset_encryption_confirmation_alert_title" = "Are you sure you want to reset your identity?"; +"screen_reset_encryption_password_subtitle" = "Confirm that you want to reset your identity."; +"screen_reset_encryption_password_title" = "Enter your account password to continue"; +"screen_reset_identity_confirmation_subtitle" = "داردید برای بازنشانی هویتتان به حساب %1$@ می‌روید. پس از آن به کاره برگردانده خواهید شد."; +"screen_reset_identity_confirmation_title" = "Can't confirm? Go to your account to reset your identity."; +"screen_room_alias_resolver_resolve_alias_failure" = "Failed to resolve room alias."; +"screen_room_attachment_source_camera" = "دوربین"; +"screen_room_attachment_source_camera_video" = "ضبط ویدیو"; +"screen_room_attachment_source_files" = "پیوست"; +"screen_room_attachment_source_gallery" = "کتابخانهٔ عکس و ویدیو"; +"screen_room_attachment_source_location" = "مکان"; +"screen_room_attachment_source_poll" = "نظرسنجی"; +"screen_room_attachment_text_formatting" = "قالب‌بندی متن"; +"screen_room_change_permissions_administrators" = "فقط مدیران"; +"screen_room_change_permissions_ban_people" = "تحریم افراد"; +"screen_room_change_permissions_delete_messages" = "برداشتن پیام‌ها"; +"screen_room_change_permissions_invite_people" = "دعوت افراد"; +"screen_room_change_permissions_moderators" = "مدیرن و ناظران"; +"screen_room_change_permissions_remove_people" = "برداشتن افراد"; +"screen_room_change_permissions_room_avatar" = "تغییر چهرک اتاق"; +"screen_room_change_permissions_room_name" = "تغییر نام اتاق"; +"screen_room_change_permissions_room_topic" = "دگرگونی موضوع اتاق"; +"screen_room_change_permissions_send_messages" = "فرستادن پیام‌ها"; +"screen_room_change_role_administrators_title" = "ویرایش مدیران"; +"screen_room_change_role_confirm_add_admin_description" = "قادر نخواهید بود این کنش را بازکردانید. داردید کاربر را به سطح قدرت خودتان ارتقا می‌دهید."; +"screen_room_change_role_confirm_add_admin_title" = "افزودن مدیر؟"; +"screen_room_change_role_confirm_demote_self_action" = "تنزل بده"; +"screen_room_change_role_confirm_demote_self_description" = "شما نمی‌توانید این تغییر را بازگردانید زیرا در حال تنزل نقش خود در اتاق هستید، اگر آخرین کاربر ممتاز در اتاق باشید، امکان دستیابی مجدد به دسترسی‌های سطح بالای اتاق غیرممکن است."; +"screen_room_change_role_confirm_demote_self_title" = "تنزل نقش شما در اتاق؟"; +"screen_room_change_role_invited_member_name" = "%1$@ (منتظر)"; +"screen_room_change_role_moderators_admin_section_footer" = "مدیران به صورت خودکار اجازه‌های نظارتی را دارند"; +"screen_room_change_role_moderators_title" = "ویرایش ناظران"; +"screen_room_change_role_unsaved_changes_description" = "تغییراتی ذخیره نشده دارید."; +"screen_room_details_add_topic_title" = "افزودن موضوع"; +"screen_room_details_already_a_member" = "از پیش عضو است"; +"screen_room_details_already_invited" = "از پیش دعوت شده"; +"screen_room_details_badge_encrypted" = "رمز شده"; +"screen_room_details_badge_not_encrypted" = "رمزنگارش نشده"; +"screen_room_details_badge_public" = "اتاق عمومی"; +"screen_room_details_edit_room_title" = "ویرایش اتاق"; +"screen_room_details_edition_error" = "خطایی ناشناخته رخ داد و اطّلاعات قابل تغییر نبودند."; +"screen_room_details_edition_error_title" = "ناتوان در به‌روز رسانی اتاق"; +"screen_room_details_encryption_enabled_subtitle" = "Messages are secured with locks. Only you and the recipients have the unique keys to unlock them."; +"screen_room_details_encryption_enabled_title" = "رمزنگاری پیام به کار افتاد"; +"screen_room_details_error_loading_notification_settings" = "An error occurred when loading notification settings."; +"screen_room_details_error_muting" = "Failed muting this room, please try again."; +"screen_room_details_error_unmuting" = "Failed unmuting this room, please try again."; +"screen_room_details_notification_mode_custom" = "سفارشی"; +"screen_room_details_notification_mode_default" = "پیش‌گزیده"; +"screen_room_details_share_room_title" = "هم‌رسانی اتاق"; +"screen_room_details_title" = "اطّلاعات اتاق"; +"screen_room_details_updating_room" = "به‌روز کردن اتاق…"; +"screen_room_directory_search_loading_error" = "شکست در بار کردن"; +"screen_room_directory_search_title" = "فهرست اتاق‌ها"; +"screen_room_encrypted_history_banner" = "Message history is currently unavailable."; +"screen_room_encrypted_history_banner_unverified" = "Message history is unavailable in this room. Verify this device to see your message history."; +"screen_room_error_failed_retrieving_user_details" = "Could not retrieve user details"; +"screen_room_invite_again_alert_message" = "می‌خواهید دوباره دعوتش کنید؟"; +"screen_room_invite_again_alert_title" = "در این گپ تنهایید"; +"screen_room_member_details_block_alert_action" = "بلوک"; +"screen_room_member_details_block_alert_description" = "Blocked users won't be able to send you messages and all their messages will be hidden. You can unblock them anytime."; +"screen_room_member_details_block_user" = "انسداد کاربر"; +"screen_room_member_details_title" = "نمایه"; +"screen_room_member_details_unblock_alert_action" = "رفع انسداد"; +"screen_room_member_details_unblock_alert_description" = "قادر خواهید بود دوباره همهٔ پیام‌هایش را ببینید."; +"screen_room_member_details_unblock_user" = "رفع انسداد کاربر"; +"screen_room_member_list_ban_member_confirmation_action" = "تحریم"; +"screen_room_member_list_ban_member_confirmation_description" = "They won’t be able to join this room again if invited."; +"screen_room_member_list_ban_member_confirmation_title" = "Are you sure you want to ban this member?"; +"screen_room_member_list_banned_empty" = "There are no banned users in this room."; +"screen_room_member_list_banning_user" = "تحریم کردن %1$@"; +"screen_room_member_list_manage_member_ban" = "برداشت و تحریم عضو"; +"screen_room_member_list_manage_member_remove" = "برداشتن از اتاق"; +"screen_room_member_list_manage_member_remove_confirmation_kick" = "تنها برداشتن عضو"; +"screen_room_member_list_manage_member_remove_confirmation_title" = "Remove member and ban from joining in the future?"; +"screen_room_member_list_manage_member_unban_action" = "رفع انسداد"; +"screen_room_member_list_manage_member_unban_message" = "They will be able to join this room again if invited."; +"screen_room_member_list_manage_member_unban_title" = "تحریم نکردن کاربر"; +"screen_room_member_list_manage_member_user_info" = "دیدن نمایه"; +"screen_room_member_list_mode_banned" = "محروم"; +"screen_room_member_list_mode_members" = "اعضا"; +"screen_room_member_list_pending_header_title" = "منتظر"; +"screen_room_member_list_removing_user" = "برداشتن %1$@…"; +"screen_room_member_list_role_administrator" = "مدیر"; +"screen_room_member_list_role_moderator" = "ناظمر"; +"screen_room_member_list_room_members_header_title" = "اعضای اتاق"; +"screen_room_member_list_unbanning_user" = "رفع تحریم %1$@"; +"screen_room_notification_settings_allow_custom" = "اجازه به تنظیمت شخصی"; +"screen_room_notification_settings_allow_custom_footnote" = "Turning this on will override your default setting"; +"screen_room_notification_settings_custom_settings_title" = "آگاهی من در این گپ برای"; +"screen_room_notification_settings_default_setting_footnote" = "می‌توانید در %1$@تان تغییرش دهید."; +"screen_room_notification_settings_default_setting_footnote_content_link" = "تنظیمات جهانی"; +"screen_room_notification_settings_default_setting_title" = "تنظیمات پیش‌گزیده"; +"screen_room_notification_settings_edit_remove_setting" = "برداشتن تنظیمات سفارشی"; +"screen_room_notification_settings_error_loading_settings" = "An error occurred while loading notification settings."; +"screen_room_notification_settings_error_restoring_default" = "Failed restoring the default mode, please try again."; +"screen_room_notification_settings_error_setting_mode" = "Failed setting the mode, please try again."; +"screen_room_notification_settings_mentions_only_disclaimer" = "Your homeserver does not support this option in encrypted rooms, you won't get notified in this room."; +"screen_room_notification_settings_mode_all_messages" = "همهٔ پیام‌ها"; +"screen_room_notification_settings_room_custom_settings_title" = "آگاهی من در این اتاق برای"; +"screen_room_retry_send_menu_send_again_action" = "فرستادن دوباره"; +"screen_room_retry_send_menu_title" = "فرستادن پیامتان شکست خورد"; +"screen_room_roles_and_permissions_admins" = "مدیران"; +"screen_room_roles_and_permissions_change_my_role" = "تغییر نقشم"; +"screen_room_roles_and_permissions_change_role_demote_to_member" = "تنزّل به عضو"; +"screen_room_roles_and_permissions_change_role_demote_to_moderator" = "تنزّل به ناظم"; +"screen_room_roles_and_permissions_member_moderation" = "نظارت اعضا"; +"screen_room_roles_and_permissions_messages_and_content" = "پیام‌ها و محتوا"; +"screen_room_roles_and_permissions_moderators" = "ناظم‌ها"; +"screen_room_roles_and_permissions_permissions_header" = "اجازه‌ها"; +"screen_room_roles_and_permissions_reset" = "بازنشانی اجازه‌ها"; +"screen_room_roles_and_permissions_reset_confirm_description" = "Once you reset permissions, you will lose the current settings."; +"screen_room_roles_and_permissions_reset_confirm_title" = "بازنشانی اجازه‌ها؟"; +"screen_room_roles_and_permissions_roles_header" = "نقش‌ها"; +"screen_room_roles_and_permissions_room_details" = "جزییات اتاق"; +"screen_room_roles_and_permissions_title" = "نقش‌ها و اجازه‌ها"; +"screen_room_timeline_add_reaction" = "افزودن شکلک"; +"screen_room_timeline_beginning_of_room" = "آغاز %1$@ است."; +"screen_room_timeline_beginning_of_room_no_name" = "این، آغاز گفت‌وگوست."; +"screen_room_timeline_less_reactions" = "نمایش کم‌تر"; +"screen_room_timeline_message_copied" = "پیام رونوشت شد"; +"screen_room_timeline_no_permission_to_post" = "اجازهٔ فرستادن به این اتاق را ندارید"; +"screen_room_timeline_reactions_show_less" = "نمایش کم‌تر"; +"screen_room_timeline_reactions_show_more" = "نمایش بیش‌تر"; +"screen_room_timeline_read_marker_title" = "جدید"; +"screen_room_title" = "گپ"; +"screen_room_typing_many_members_first_component_ios" = "%1$@، %2$@ و "; +"screen_room_typing_notification_plural_ios" = " دارند می‌نویسند…"; +"screen_room_typing_notification_singular_ios" = " دارد می‌نویسد…"; +"screen_room_typing_two_members" = "%1$@ و %2$@"; +"screen_room_voice_message_tooltip" = "نگه داشتن برای ضبط"; +"screen_roomlist_a11y_create_message" = "ایجاد اتاق یا گفت‌وگویی جدید"; +"screen_roomlist_empty_message" = "آغاز با پیام دادن به کسی."; +"screen_roomlist_empty_title" = "هنوز گپی وجود ندارد."; +"screen_roomlist_filter_favourites" = "علاقه‌مندی‌ها"; +"screen_roomlist_filter_favourites_empty_state_subtitle" = "You can add a chat to your favourites in the chat settings.\nFor now, you can deselect filters in order to see your other chats"; +"screen_roomlist_filter_favourites_empty_state_title" = "هنوز هیچ گپ مورد علاقه‌ای ندارید"; +"screen_roomlist_filter_invites" = "دعوت‌ها"; +"screen_roomlist_filter_invites_empty_state_title" = "هیچ دعوت منتظری ندارید."; +"screen_roomlist_filter_low_priority" = "اولویت کم"; +"screen_roomlist_filter_mixed_empty_state_subtitle" = "می توانید پالایه‌ها را برای دیدن دیگر گپ‌هایتان بردارید"; +"screen_roomlist_filter_mixed_empty_state_title" = "هیچ گپی برای این گزینش ندارید"; +"screen_roomlist_filter_people_empty_state_title" = "هنوز هیچ پیام مستقیمی ندارید"; +"screen_roomlist_filter_rooms" = "اتاق‌ها"; +"screen_roomlist_filter_rooms_empty_state_title" = "هنوز در هیچ اتاقی نیستید"; +"screen_roomlist_filter_unreads" = "نخوانده‌ها"; +"screen_roomlist_filter_unreads_empty_state_title" = "تبریک!\nهیچ پیام نخوانده‌ای ندارید!"; +"screen_roomlist_main_space_title" = "گپ‌ها"; +"screen_roomlist_mark_as_read" = "علامت‌گذاری به عنوان خوانده شده"; +"screen_roomlist_mark_as_unread" = "نشان به ناخوانده"; +"screen_roomlist_room_directory_button_title" = "مرور همهٔ اتاق‌ها"; +"screen_server_confirmation_message_login_element_dot_io" = "A private server for Element employees."; +"screen_server_confirmation_message_login_matrix_dot_org" = "ماتریکس شبکه‌ای بار برای ارتباطات نامتمرکز و امن است."; +"screen_server_confirmation_message_register" = "جایی که گفت‌وگوهایتان خواهند زیست — درست مثل استفاده‌تان از فراهم کنندهٔ رایانامه‌ای برای نگه داشتن رایانامه‌هایتان."; +"screen_server_confirmation_title_login" = "دارید به %1$@ وارد می‌شوید"; +"screen_server_confirmation_title_register" = "دارید روی %1$@ حساب می‌سازید"; +"screen_session_verification_cancelled_subtitle" = "Something doesn’t seem right. Either the request timed out or the request was denied."; +"screen_session_verification_compare_emojis_subtitle" = "Confirm that the emojis below match those shown on your other session."; +"screen_session_verification_compare_emojis_title" = "مقایسهٔ شکلک‌ها"; +"screen_session_verification_compare_numbers_subtitle" = "Confirm that the numbers below match those shown on your other session."; +"screen_session_verification_compare_numbers_title" = "مقایسهٔ اعداد"; +"screen_session_verification_complete_subtitle" = "نشست جدید شما اکنون تأیید شده‌است. این نشست به پیام‌های رمزگذاری شده‌ی شما دسترسی داشته و سایر کاربران نیز این نشست را قابل اعتماد می دانند."; +"screen_session_verification_enter_recovery_key" = "ورود کلید بازیابی"; +"screen_session_verification_open_existing_session_subtitle" = "Prove it’s you in order to access your encrypted message history."; +"screen_session_verification_open_existing_session_title" = "گشودن نشستی موجود"; +"screen_session_verification_positive_button_canceled" = "تلاش برای تأیید دوباره"; +"screen_session_verification_positive_button_initial" = "آماده‌ام"; +"screen_session_verification_positive_button_verifying_ongoing" = "منتظر تطابق"; +"screen_session_verification_ready_subtitle" = "مقایسهٔ مجموعه‌ای یکتا از شکلک‌ها."; +"screen_session_verification_request_accepted_subtitle" = "شکلک‌ها را مقایسه کنید، از ترتیب نمایش آنان نیز مطمئن شوید."; +"screen_session_verification_they_dont_match" = "مطابق نیستند"; +"screen_session_verification_they_match" = "مطابقند"; +"screen_session_verification_waiting_to_accept_subtitle" = "Accept the request to start the verification process in your other session to continue."; +"screen_session_verification_waiting_to_accept_title" = "منظر پذیرش درخواست"; +"screen_share_location_title" = "هم‌رسانی موقعیت"; +"screen_share_my_location_action" = "هم‌رسانی مکانم"; +"screen_share_open_apple_maps" = "گشودن در نقشه‌های اپل"; +"screen_share_open_google_maps" = "گشودن در نقشه‌های گوگل"; +"screen_share_open_osm_maps" = "گشودن در اوپن‌استریت‌مپ"; +"screen_share_this_location_action" = "هم‌رسانی این مکان"; +"screen_signed_out_reason_1" = "گذرواژه‌تان را در نشستی دیگر تغییر داده‌اید"; +"screen_signed_out_reason_2" = "این نشست را از نشستی دیگر حذف کرده‌اید"; +"screen_signed_out_reason_3" = "Your server’s administrator has invalidated your access"; +"screen_signed_out_subtitle" = "You might have been signed out for one of the reasons listed below. Please sign in again to continue using %@."; +"screen_signed_out_title" = "خارج شده‌اید"; +"screen_signout_confirmation_dialog_content" = "مطمئنید که می‌خواهید از حسابتان خارج شوید؟"; +"screen_signout_in_progress_dialog_content" = "خارج شدن…"; +"screen_signout_key_backup_disabled_subtitle" = "You are about to sign out of your last session. If you sign out now, you will lose access to your encrypted messages."; +"screen_signout_key_backup_disabled_title" = "پشتیبان را خاموش کرده‌اید"; +"screen_signout_key_backup_offline_subtitle" = "Your keys were still being backed up when you went offline. Reconnect so that your keys can be backed up before signing out."; +"screen_signout_key_backup_ongoing_subtitle" = "لطفاً پیش از خروج منتظر پایانش شوید."; +"screen_signout_key_backup_ongoing_title" = "کلیدهایتان هنوز در حال پشتیبان گیریند"; +"screen_signout_recovery_disabled_subtitle" = "You are about to sign out of your last session. If you sign out now, you'll lose access to your encrypted messages."; +"screen_signout_recovery_disabled_title" = "بازگردانی برپا نشده"; +"screen_signout_save_recovery_key_subtitle" = "You are about to sign out of your last session. If you sign out now, you might lose access to your encrypted messages."; +"screen_start_chat_error_starting_chat" = "An error occurred when trying to start a chat"; +"screen_view_location_title" = "مکان"; +"screen_welcome_bullet_1" = "Calls, polls, search and more will be added later this year."; +"screen_welcome_bullet_2" = "Message history for encrypted rooms isn’t available yet."; +"screen_welcome_bullet_3" = "We’d love to hear from you, let us know what you think via the settings page."; +"screen_welcome_button" = "بزن بریم!"; +"screen_welcome_subtitle" = "چیزهایی که باید بدانید:"; +"screen_welcome_title" = "به %1$@ خوش آمدید!"; +"session_verification_banner_message" = "Looks like you’re using a new device. Verify with another device to access your encrypted messages."; +"session_verification_banner_title" = "تأیید کنید که خودتانید"; +"settings_rageshake" = "تکان دادن"; +"settings_rageshake_detection_threshold" = "آستانهٔ تشخیص"; +"settings_version_number" = "نگارش : %1$@ (%2$@)"; +"state_event_avatar_changed_too" = "(چهرک نیز تغییر کرد)"; +"state_event_avatar_url_changed" = "%1$@ چهرکش را تغییر داد"; +"state_event_avatar_url_changed_by_you" = "چهرکتان را تغییر دادید"; +"state_event_demoted_to_member" = "%1$@ به عضو تنزّل یافت"; +"state_event_demoted_to_moderator" = "%1$@ به ناظم تنزّل یافت"; +"state_event_display_name_changed_from" = "%1$@ نام نمایشیش را از %2$@ به%3$@ تغییر داد"; +"state_event_display_name_changed_from_by_you" = "نام نمایشیتان را از %1$@ به %2$@ تغییر دادید"; +"state_event_display_name_removed" = "%1$@ نام نمایشیش را برداشت (%2$@ بود)"; +"state_event_display_name_removed_by_you" = "نام نمایشیتان را برداشتید (%1$@ بود)"; +"state_event_display_name_set" = "%1$@ نام نمایشیش را به %2$@ تغییر داد"; +"state_event_display_name_set_by_you" = "نام نمایشیتان را به %1$@ تغییر دادید"; +"state_event_promoted_to_administrator" = "%1$@ به مدیر ارتقا یافت"; +"state_event_promoted_to_moderator" = "%1$@ به ناظم ارتقا یافت"; +"state_event_room_avatar_changed" = "%1$@ چهرک اتاق را تغییر داد"; +"state_event_room_avatar_changed_by_you" = "چهرک اتاق را تغییر دادید"; +"state_event_room_avatar_removed" = "%1$@ چهرک اتاق را برداشت"; +"state_event_room_avatar_removed_by_you" = "چهرک اتاق را برداشتید"; +"state_event_room_ban" = "%2$@ به دست %1$@ مسدود کرد"; +"state_event_room_ban_by_you" = "%1$@ را مسدود کردید"; +"state_event_room_created" = "%1$@ اتاق را ایجاد کرد"; +"state_event_room_created_by_you" = "اتاق را ساختید"; +"state_event_room_invite" = "%1$@ از %2$@ دعوت کرد"; +"state_event_room_invite_accepted" = "%1$@ دعوت را پذیرفت"; +"state_event_room_invite_accepted_by_you" = "دعوت را پذیرفتید"; +"state_event_room_invite_by_you" = "از %1$@ دعوت کردید"; +"state_event_room_invite_you" = "%1$@ دعوتتان کرد"; +"state_event_room_join" = "%1$@ به اتاق پیوست"; +"state_event_room_join_by_you" = "به اتاق پیوستید"; +"state_event_room_knock" = "%1$@ درخواست پیوستن کرد"; +"state_event_room_knock_accepted" = "%1$@ گذاشت %2$@ بپیوندد"; +"state_event_room_knock_accepted_by_you" = "گذاشتید %1$@ بپیوندد"; +"state_event_room_knock_by_you" = "درخواست پیوستن کردید"; +"state_event_room_knock_denied" = "درخواست پیوستن %2$@ به دست %1$@ لغو شد"; +"state_event_room_knock_denied_by_you" = "درخوسات پیوستن %1$@ را رد کردید"; +"state_event_room_knock_denied_you" = "درخواست پیوستنتان به دست %1$@ رد شد"; +"state_event_room_knock_retracted" = "%1$@ دیگر علاقه‌ای به پیوستن ندارد"; +"state_event_room_knock_retracted_by_you" = "درخواست پیوستنتان را لغو کردید"; +"state_event_room_leave" = "%1$@ اتاق را ترک کرد"; +"state_event_room_leave_by_you" = "اتاق را ترک کردید"; +"state_event_room_name_changed" = "%1$@ نام اتاق را تغییر داد: %2$@"; +"state_event_room_name_changed_by_you" = "نام اتاق را تغییر دادید: %1$@"; +"state_event_room_name_removed" = "%1$@نام اتاق را برداشت"; +"state_event_room_name_removed_by_you" = "نام اتاق را برداشتید"; +"state_event_room_none" = "%1$@ تغییری ایجاد نکرد"; +"state_event_room_none_by_you" = "تغییری ایجاد نکردید"; +"state_event_room_pinned_events_changed" = "%1$@ پیام‌های سنجاق شده را تغییر داد"; +"state_event_room_pinned_events_changed_by_you" = "پیام‌های سنجاق شده را تغییر دادید"; +"state_event_room_pinned_events_pinned" = "%1$@ پیامی را سنجاق کرد"; +"state_event_room_pinned_events_pinned_by_you" = "پیامی را سنجاق کردید"; +"state_event_room_pinned_events_unpinned" = "%1$@ سنجاق پیامی را برداشت"; +"state_event_room_pinned_events_unpinned_by_you" = "سنجاق پیامی را برداشتید"; +"state_event_room_reject" = "%1$@ دعوت را رد کرد"; +"state_event_room_reject_by_you" = "دعوت را رد کردید"; +"state_event_room_remove" = "%2$@ به دست %1$@ برداشته شد"; +"state_event_room_remove_by_you" = "%1$@ را برداشتید"; +"state_event_room_third_party_invite" = "%1$@ دعوتی برای پیوستن %2$@ به اتاق فرستاد"; +"state_event_room_third_party_invite_by_you" = "برای %1$@ دعوت پیوستن به اتاق فرستادید"; +"state_event_room_third_party_revoked_invite" = "%1$@ دعوت پیوستن به اتاق %2$@ را باطل کرد"; +"state_event_room_third_party_revoked_invite_by_you" = "دعوت پیوستن %1$@ به اتاق را پس گرفتید"; +"state_event_room_topic_changed" = "%1$@ موضوع را تغییر داد: %2$@"; +"state_event_room_topic_changed_by_you" = "موضوع را تغییر دادید: %1$@"; +"state_event_room_topic_removed" = "%1$@موضوع اتاق را برداشت"; +"state_event_room_topic_removed_by_you" = "موضوع اتاق را برداشتید"; +"state_event_room_unban" = "%1$@ انسداد %2$@ را لغو کرد"; +"state_event_room_unban_by_you" = "انسداد%1$@ را لغو کردید"; +"state_event_room_unknown_membership_change" = "%1$@ تغییری نامعلوم در عضویتش داد"; +"test_language_identifier" = "fa"; +"test_untranslated_default_language_identifier" = "en"; +"troubleshoot_notifications_entry_point_section" = "رفع‌اشکال"; +"troubleshoot_notifications_screen_action" = "اجرای آزمون‌ها"; +"troubleshoot_notifications_screen_action_again" = "اجرای دوبارهٔ آزمون‌ها"; +"troubleshoot_notifications_screen_failure" = "برخی آزمون‌ها شکست خوردند. بررسی جزییات."; +"troubleshoot_notifications_screen_notice" = "Run the tests to detect any issue in your configuration that may make notifications not behave as expected."; +"troubleshoot_notifications_screen_quick_fix_action" = "تلاش برای تعمیر"; +"troubleshoot_notifications_screen_success" = "همهٔ آزمون‌ها با موفّقیت گذرانده شدند."; +"troubleshoot_notifications_screen_title" = "رفع‌اشکال آگاهی‌ها"; +"troubleshoot_notifications_screen_waiting" = "Some tests require your attention. Please check the details."; +"troubleshoot_notifications_test_check_permission_description" = "Check that the application can show notifications."; +"troubleshoot_notifications_test_check_permission_title" = "بررسی اجازه‌ها"; +"troubleshoot_notifications_test_current_push_provider_description" = "Get the name of the current provider."; +"troubleshoot_notifications_test_current_push_provider_failure" = "No push providers selected."; +"troubleshoot_notifications_test_current_push_provider_success" = "Current push provider: %1$@."; +"troubleshoot_notifications_test_current_push_provider_title" = "Current push provider"; +"troubleshoot_notifications_test_detect_push_provider_description" = "Ensure that the application has at least one push provider."; +"troubleshoot_notifications_test_detect_push_provider_failure" = "No push providers found."; +"troubleshoot_notifications_test_detect_push_provider_title" = "Detect push providers"; +"troubleshoot_notifications_test_display_notification_description" = "Check that the application can display notification."; +"troubleshoot_notifications_test_display_notification_failure" = "The notification has not been clicked."; +"troubleshoot_notifications_test_display_notification_permission_failure" = "Cannot display the notification."; +"troubleshoot_notifications_test_display_notification_success" = "روی آگاهی کلیک شد!"; +"troubleshoot_notifications_test_display_notification_title" = "Display notification"; +"troubleshoot_notifications_test_display_notification_waiting" = "Please click on the notification to continue the test."; +"troubleshoot_notifications_test_firebase_availability_description" = "Ensure that Firebase is available."; +"troubleshoot_notifications_test_firebase_availability_failure" = "Firebase is not available."; +"troubleshoot_notifications_test_firebase_availability_success" = "Firebase is available."; +"troubleshoot_notifications_test_firebase_availability_title" = "Check Firebase"; +"troubleshoot_notifications_test_firebase_token_description" = "Ensure that Firebase token is available."; +"troubleshoot_notifications_test_firebase_token_failure" = "Firebase token is not known."; +"troubleshoot_notifications_test_firebase_token_success" = "Firebase token: %1$@."; +"troubleshoot_notifications_test_firebase_token_title" = "Check Firebase token"; +"troubleshoot_notifications_test_push_loop_back_description" = "Ensure that the application is receiving push."; +"troubleshoot_notifications_test_push_loop_back_failure_1" = "خطا: فرستنده درخواست را رد کرد."; +"troubleshoot_notifications_test_push_loop_back_failure_2" = "خطا: %1$@."; +"troubleshoot_notifications_test_push_loop_back_failure_3" = "خطا. نتوانست فرستادن را بیازماید."; +"troubleshoot_notifications_test_push_loop_back_failure_4" = "خطا. مهلت زمانی انتظار برای فرستادن سر رسید."; +"troubleshoot_notifications_test_push_loop_back_success" = "Push loop back took %1$d ms."; +"troubleshoot_notifications_test_push_loop_back_title" = "Test Push loop back"; +"troubleshoot_notifications_test_unified_push_description" = "Ensure that UnifiedPush distributors are available."; +"troubleshoot_notifications_test_unified_push_failure" = "No push distributors found."; +"troubleshoot_notifications_test_unified_push_title" = "Check UnifiedPush"; +"a11y_poll" = "نظرسنجی"; +"dialog_title_error" = "خطا"; +"dialog_title_success" = "موفّقیت"; +"notification_fallback_content" = "آگاهی"; +"notification_invitation_action_join" = "پیوستن"; +"notification_invitation_action_reject" = "رد کردن"; +"notification_room_action_mark_as_read" = "علامت‌گذاری به عنوان خوانده شده"; +"notification_room_action_quick_reply" = "پاسخ سریع"; +"screen_pinned_timeline_screen_title_empty" = "پیام‌های سنجاق شده"; +"screen_room_mentions_at_room_title" = "هرکسی"; +"screen_account_provider_change" = "تغییر فراهم کنندهٔ حساب"; +"screen_account_provider_signin_subtitle" = "جایی که گفت‌وگوهایتان خواهند زیست — درست مثل استفاده‌تان از فراهم کنندهٔ رایانامه‌ای برای نگه داشتن رایانامه‌هایتان."; +"screen_account_provider_signup_subtitle" = "جایی که گفت‌وگوهایتان خواهند زیست — درست مثل استفاده‌تان از فراهم کنندهٔ رایانامه‌ای برای نگه داشتن رایانامه‌هایتان."; +"screen_analytics_settings_help_us_improve" = "Share anonymous usage data to help us identify issues."; +"screen_analytics_settings_read_terms" = "You can read all our terms %1$@."; +"screen_analytics_settings_read_terms_content_link" = "این‌جا"; +"screen_blocked_users_unblock_alert_action" = "رفع انسداد"; +"screen_blocked_users_unblock_alert_description" = "قادر خواهید بود دوباره همهٔ پیام‌هایش را ببینید."; +"screen_blocked_users_unblock_alert_title" = "رفع انسداد کاربر"; +"screen_bug_report_rash_logs_alert_title" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; +"screen_chat_backup_recovery_action_confirm" = "ورود کلید بازیابی"; +"screen_create_poll_cancel_confirmation_content_ios" = "تغییراتتان ذخیره نمی‌شوند"; +"screen_create_room_add_people_title" = "دعوت افراد"; +"screen_create_room_room_name_label" = "نام اتاق"; +"screen_create_room_title" = "ایجاد اتاق"; +"screen_dm_details_block_alert_action" = "بلوک"; +"screen_dm_details_block_alert_description" = "Blocked users won't be able to send you messages and all their messages will be hidden. You can unblock them anytime."; +"screen_dm_details_block_user" = "انسداد کاربر"; +"screen_dm_details_unblock_alert_action" = "رفع انسداد"; +"screen_dm_details_unblock_alert_description" = "قادر خواهید بود دوباره همهٔ پیام‌هایش را ببینید."; +"screen_dm_details_unblock_user" = "رفع انسداد کاربر"; +"screen_edit_poll_delete_confirmation_title" = "حذف نظرسنجی"; +"screen_edit_poll_title" = "ویرایش نظرسنجی"; +"screen_identity_use_another_device" = "استفاده از افزاره‌ای دیگر"; +"screen_login_subtitle" = "ماتریکس شبکه‌ای بار برای ارتباطات نامتمرکز و امن است."; +"screen_notification_settings_mentions_section_title" = "اشاره‌ها"; +"screen_qr_code_login_invalid_scan_state_retry_button" = "تلاش دوباره"; +"screen_recovery_key_confirm_title" = "ورود کلید بازیابیتان"; +"screen_report_content_block_user" = "انسداد کاربر"; +"screen_reset_encryption_password_placeholder" = "ورود…"; +"screen_room_attachment_source_camera_photo" = "عکس گرفتن"; +"screen_room_change_permissions_everyone" = "هرکسی"; +"screen_room_change_permissions_member_moderation" = "نظارت اعضا"; +"screen_room_change_permissions_messages_and_content" = "پیام‌ها و محتوا"; +"screen_room_change_permissions_room_details" = "جزییات اتاق"; +"screen_room_change_role_section_administrators" = "مدیران"; +"screen_room_change_role_section_moderators" = "ناظم‌ها"; +"screen_room_change_role_section_users" = "اعضا"; +"screen_room_change_role_unsaved_changes_title" = "ذخیرهٔ تغییرات؟"; +"screen_room_details_invite_people_title" = "دعوت افراد"; +"screen_room_details_leave_conversation_title" = "ترک گفت‌وگو"; +"screen_room_details_leave_room_title" = "ترک اتاق"; +"screen_room_details_notification_title" = "آگاهی‌ها"; +"screen_room_details_roles_and_permissions" = "نقش‌ها و اجازه‌ها"; +"screen_room_details_room_name_label" = "نام اتاق"; +"screen_room_details_security_title" = "امنیت"; +"screen_room_details_topic_title" = "موضوع"; +"screen_room_error_failed_processing_media" = "پردازش رسانه برای بارگذاری شکست خورد. لطفاً دوباره تلاش کنید."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "برداشت و تحریم عضو"; +"screen_room_notification_settings_mode_mentions_and_keywords" = "فقط اشاره‌ها و کلیدواژگان"; +"screen_roomlist_filter_people" = "افراد"; +"screen_server_confirmation_change_server" = "تغییر فراهم کنندهٔ حساب"; +"screen_signout_confirmation_dialog_submit" = "خروج"; +"screen_signout_confirmation_dialog_title" = "خروج"; +"screen_signout_key_backup_offline_title" = "کلیدهایتان هنوز در حال پشتیبان گیریند"; +"screen_signout_preference_item" = "خروج"; +"screen_signout_save_recovery_key_title" = "کلید بازیابیتان را ذخیره کرده‌اید؟"; +"troubleshoot_notifications_entry_point_title" = "رفع‌اشکال آگاهی‌ها"; diff --git a/ElementX/Resources/Localizations/fa.lproj/SAS.strings b/ElementX/Resources/Localizations/fa.lproj/SAS.strings new file mode 100644 index 0000000000..42b46e16d6 --- /dev/null +++ b/ElementX/Resources/Localizations/fa.lproj/SAS.strings @@ -0,0 +1,64 @@ +"aeroplane" = "هواپیما"; +"anchor" = "لنگر"; +"apple" = "سیب"; +"ball" = "توپ"; +"banana" = "موز"; +"bell" = "زنگ"; +"bicycle" = "دوچرخه"; +"book" = "کتاب"; +"butterfly" = "پروانه"; +"cactus" = "کاکتوس"; +"cake" = "کیک"; +"cat" = "گربه"; +"clock" = "ساعت"; +"cloud" = "ابر"; +"corn" = "ذرت"; +"dog" = "سگ"; +"elephant" = "فیل"; +"fire" = "آتش"; +"fish" = "ماهی"; +"flag" = "پرچم"; +"flower" = "گل"; +"folder" = "پوشه"; +"gift" = "هدیه"; +"glasses" = "عینک"; +"globe" = "زمین"; +"guitar" = "گیتار"; +"hammer" = "چکش"; +"hat" = "کلاه"; +"headphones" = "هدفون"; +"heart" = "قلب"; +"horse" = "اسب"; +"hourglass" = "ساعت شنی"; +"key" = "کلید"; +"light_bulb" = "لامپ"; +"lion" = "شیر"; +"lock" = "قفل"; +"moon" = "ماه"; +"mushroom" = "قارچ"; +"octopus" = "اختاپوس"; +"panda" = "پاندا"; +"paperclip" = "گیره کاغذ"; +"pencil" = "مداد"; +"penguin" = "پنگوئن"; +"pig" = "خوک"; +"pin" = "سنجاق"; +"pizza" = "پیتزا"; +"rabbit" = "خرگوش"; +"robot" = "ربات"; +"rocket" = "موشک"; +"rooster" = "خروس"; +"santa" = "بابا نوئل"; +"scissors" = "قیچی"; +"smiley" = "خنده"; +"spanner" = "آچار"; +"strawberry" = "توت فرنگی"; +"telephone" = "تلفن"; +"thumbs_up" = "لایک"; +"train" = "قطار"; +"tree" = "درخت"; +"trophy" = "جام"; +"trumpet" = "شیپور"; +"turtle" = "لاک‌پشت"; +"umbrella" = "چتر"; +"unicorn" = "تک شاخ"; \ No newline at end of file diff --git a/ElementX/Resources/Localizations/fr.lproj/Localizable.strings b/ElementX/Resources/Localizations/fr.lproj/Localizable.strings index 9cb2420158..3a6034d05f 100644 --- a/ElementX/Resources/Localizations/fr.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/fr.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Pause"; "a11y_pin_field" = "Code PIN"; "a11y_play" = "Lecture"; -"a11y_poll" = "Sondage"; "a11y_poll_end" = "Sondage terminé"; "a11y_react_with" = "Réagir avec %1$@"; "a11y_react_with_other_emojis" = "Réagir avec d’autres emojis"; @@ -41,6 +40,7 @@ "action_create" = "Créer"; "action_create_a_room" = "Créer un salon"; "action_deactivate" = "Désactiver"; +"action_deactivate_account" = "Désactiver le compte"; "action_decline" = "Refuser"; "action_delete_poll" = "Supprimer le sondage"; "action_disable" = "Désactiver"; @@ -64,6 +64,7 @@ "action_leave" = "Quitter"; "action_leave_conversation" = "Quitter la discussion"; "action_leave_room" = "Quitter le salon"; +"action_load_more" = "Voir plus"; "action_manage_account" = "Gérer le compte"; "action_manage_devices" = "Gérez les sessions"; "action_message" = "Message"; @@ -84,7 +85,7 @@ "action_report_bug" = "Signaler un problème"; "action_report_content" = "Signaler le contenu"; "action_reset" = "Réinitialiser"; -"action_reset_identity" = "Reset identity"; +"action_reset_identity" = "Réinitialiser l'identité"; "action_retry" = "Réessayer"; "action_retry_decryption" = "Réessayer le déchiffrement"; "action_save" = "Enregistrer"; @@ -93,6 +94,7 @@ "action_send_message" = "Envoyer un message"; "action_share" = "Partager"; "action_share_link" = "Partager le lien"; +"action_show" = "Show"; "action_sign_in_again" = "Se connecter à nouveau"; "action_signout" = "Se déconnecter"; "action_signout_anyway" = "Se déconnecter quand même"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "Voir dans la discussion"; "action_view_source" = "Afficher la source"; "action_yes" = "Oui"; -"action.load_more" = "Voir plus"; -"action_deactivate_account" = "Désactiver le compte"; "banner_migrate_to_native_sliding_sync_action" = "Déconnecter et mettre à niveau"; "banner_migrate_to_native_sliding_sync_description" = "Votre serveur prend désormais en charge un nouveau protocole plus rapide. Déconnectez-vous, puis reconnectez-vous pour effectuer la mise à niveau dès maintenant. En le faisant tout de suite, vous éviterez une déconnexion forcée lorsque l'ancien protocole sera supprimé."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Votre serveur d’accueil ne prend plus en charge l'ancien protocole. Veuillez vous déconnecter puis vous reconnecter pour continuer à utiliser l'application."; @@ -162,6 +162,7 @@ "common_modern" = "Moderne"; "common_mute" = "Mettre en sourdine"; "common_no_results" = "Aucun résultat"; +"common_no_room_name" = "Salon sans nom"; "common_offline" = "Hors ligne"; "common_optic_id_ios" = "Optic ID"; "common_or" = "ou"; @@ -170,6 +171,8 @@ "common_permalink" = "Permalien"; "common_permission" = "Autorisation"; "common_please_wait" = "Veuillez patienter…"; +"common_poll_end_confirmation" = "Êtes-vous sûr de vouloir mettre fin à ce sondage ?"; +"common_poll_summary" = "Sondage : %1$@"; "common_poll_total_votes" = "Nombre total de votes : %1$@"; "common_poll_undisclosed_text" = "Les résultats s’afficheront une fois le sondage terminé"; "common_privacy_policy" = "Politique de confidentialité"; @@ -200,6 +203,7 @@ "common_settings" = "Paramètres"; "common_shared_location" = "Position partagée"; "common_signing_out" = "Déconnexion"; +"common_something_went_wrong" = "Une erreur s'est produite"; "common_starting_chat" = "Création de la discussion..."; "common_sticker" = "Autocollant"; "common_success" = "Succès"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "De quoi s’agit-il dans ce salon ?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Échec de déchiffrement"; +"common_unable_to_decrypt_no_access" = "Vous ne pouvez pas voir ce message"; "common_unable_to_invite_message" = "Les invitations n’ont pas pu être envoyées à un ou plusieurs utilisateurs."; "common_unable_to_invite_title" = "Impossible d’envoyer une ou plusieurs invitations"; "common_unlock" = "Déverrouillage"; @@ -221,24 +226,21 @@ "common_username" = "Nom d’utilisateur"; "common_verification_cancelled" = "Vérification annulée"; "common_verification_complete" = "Vérification terminée"; +"common_verify_device" = "Vérifier la session"; "common_video" = "Vidéo"; "common_voice_message" = "Message vocal"; "common_waiting" = "En attente..."; "common_waiting_for_decryption_key" = "En attente de la clé de déchiffrement"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "Ne plus afficher"; "common.open_source_licenses" = "Licences open source"; "common.pinned" = "Épinglé"; "common.send_to" = "Envoyer vers"; "common.you" = "Vous"; -"common_no_room_name" = "Salon sans nom"; -"common_poll_end_confirmation" = "Êtes-vous sûr de vouloir mettre fin à ce sondage ?"; -"common_poll_summary" = "Sondage : %1$@"; -"common_something_went_wrong" = "Une erreur s'est produite"; -"common_unable_to_decrypt_no_access" = "Vous ne pouvez pas voir ce message"; -"common_verify_device" = "Vérifier la session"; "confirm_recovery_key_banner_message" = "La sauvegarde des conversations est désynchronisée. Vous devez confirmer la clé de récupération pour accéder à votre historique."; "confirm_recovery_key_banner_title" = "Confirmer votre clé de récupération"; "crash_detection_dialog_content" = "%1$@ s’est arrêté la dernière fois qu’il a été utilisé. Souhaitez-vous partager un rapport d’incident avec nous ?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "Pour permettre à l’application d’utiliser l’appareil photo, veuillez accorder l’autorisation dans les paramètres du système."; "dialog_permission_generic" = "Veuillez accorder l’autorisation dans les paramètres du système."; "dialog_permission_location_description_ios" = "Autorisez l’accès dans Paramètres / Localisation."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "Notifications silencieuses"; "notification_incoming_call" = "Appel entrant"; "notification_inline_reply_failed" = "** Échec de l’envoi - veuillez ouvrir le salon"; -"notification_invitation_action_reject" = "Rejeter"; "notification_invite_body" = "Vous a invité(e) à discuter"; "notification_invite_body_with_sender" = "%1$@ vous a invité à discuter"; "notification_mentioned_you_body" = "Mentionné(e): %1$@"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "URL invalide, assurez-vous d’inclure le protocol (http/https) et l’adresse correcte."; "screen_pinned_timeline_empty_state_description" = "Cliquez (clic long) sur un message et choisissez « %1$@ » pour qu‘il apparaisse ici."; "screen_pinned_timeline_empty_state_headline" = "Épinglez les messages importants pour leur donner plus de visibilité"; -"screen_pinned_timeline_screen_title_empty" = "Messages épinglés"; "screen_reset_encryption_password_error" = "Une erreur s'est produite. Vérifiez que le mot de passe de votre compte est correct et réessayez."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Révoquer la verification et envoyer"; "screen_resolve_send_failure_changed_identity_subtitle" = "Vous pouvez révoquer la verification et envoyer ce message, ou vous pouvez annuler pour l'instant et réessayer plus tard après avoir vérifié à nouveau %1$@."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Le message n'a pas été envoyé car l'identité vérifiée de %1$@ a changé."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Le message n'a pas été envoyé car %1$@ n'a pas vérifié tous ses appareils."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message non envoyé car vous n'avez pas vérifié tous vos appareils."; -"screen_account_provider_change" = "Changer de fournisseur de compte"; "screen_account_provider_form_hint" = "Adresse du serveur d’accueil"; "screen_account_provider_form_notice" = "Entrez un terme de recherche ou une adresse de domaine."; "screen_account_provider_form_subtitle" = "Recherchez une entreprise, une communauté ou un serveur privé."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "La sauvegarde assure que vous ne perdiez pas l’historique des discussions. %1$@."; "screen_chat_backup_key_backup_title" = "Sauvegarde"; "screen_chat_backup_recovery_action_change" = "Changer la clé de récupération"; -"screen_chat_backup_recovery_action_confirm" = "Confirmer la clé de récupération"; "screen_chat_backup_recovery_action_confirm_description" = "La sauvegarde des discussions est désynchronisée."; "screen_chat_backup_recovery_action_setup" = "Configurer la récupération"; "screen_chat_backup_recovery_action_setup_description" = "Accédez à vos messages chiffrés si vous perdez tous vos appareils ou que vous êtes déconnectés de %1$@ partout."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Afficher les résultats uniquement après la fin du sondage"; "screen_create_poll_anonymous_headline" = "Masquer les votes"; "screen_create_poll_answer_hint" = "Option %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Vos modifications ne seront pas enregistrées"; "screen_create_poll_cancel_confirmation_title_ios" = "Annuler le sondage"; "screen_create_poll_question_desc" = "Question ou sujet"; "screen_create_poll_question_hint" = "Quel est le sujet du sondage ?"; @@ -483,7 +480,7 @@ "screen_encryption_reset_bullet_2" = "Vous perdrez l’historique de vos messages"; "screen_encryption_reset_bullet_3" = "Vous devrez vérifier à nouveau tous vos appareils et tous vos contacts"; "screen_encryption_reset_footer" = "Ne réinitialisez votre identité que si vous n'avez plus accès à aucune autre session et que vous avez perdu votre clé de récupération."; -"screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; +"screen_encryption_reset_title" = "Vous ne pouvez pas confirmer ? Vous devez réinitialiser votre identité."; "screen_identity_confirmation_cannot_confirm" = "Confirmation impossible ?"; "screen_identity_confirmation_create_new_recovery_key" = "Créer une nouvelle clé de récupération"; "screen_identity_confirmation_subtitle" = "Vérifier cette session pour configurer votre messagerie sécurisée."; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Discussions de groupe"; "screen_notification_settings_invite_for_me_label" = "Invitations"; "screen_notification_settings_mentions_only_disclaimer" = "Votre serveur d’accueil ne supporte pas cette option pour les salons chiffrés, vous pourriez ne pas être notifié(e) dans certains salons."; -"screen_notification_settings_mentions_section_title" = "Mentions"; "screen_notification_settings_mode_all" = "Tous"; "screen_notification_settings_mode_mentions" = "Mentions"; "screen_notification_settings_notification_section_title" = "Prévenez-moi pour"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Saisissez la clé ici…"; "screen_recovery_key_confirm_lost_recovery_key" = "Clé de récupération perdue?"; "screen_recovery_key_confirm_success" = "Clé de récupération confirmée"; -"screen_recovery_key_confirm_title" = "Saisissez votre clé de récupération"; "screen_recovery_key_copied_to_clipboard" = "Clé de récupération copiée"; "screen_recovery_key_generating_key" = "Génération…"; "screen_recovery_key_save_action" = "Enregistrer la clé"; @@ -636,8 +631,7 @@ "screen_report_content_hint" = "Raison du signalement de ce contenu"; "screen_reset_encryption_confirmation_alert_action" = "Oui, réinitialisez maintenant"; "screen_reset_encryption_confirmation_alert_subtitle" = "Cette opération ne peut pas être annulée."; -"screen_reset_encryption_confirmation_alert_title" = "Are you sure you want to reset your identity?"; -"screen_reset_encryption_password_placeholder" = "Saisissez..."; +"screen_reset_encryption_confirmation_alert_title" = "Êtes-vous sûr de vouloir réinitialiser votre identité ?"; "screen_reset_encryption_password_subtitle" = "Veuillez confirmer que vous souhaitez réinitialiser votre identité."; "screen_reset_encryption_password_title" = "Saisissez le mot de passe de votre compte pour continuer"; "screen_reset_identity_confirmation_subtitle" = "Vous êtes sur le point d'accéder à votre compte %1$@ pour réinitialiser votre identité. Vous serez ensuite redirigé vers l'application."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Les administrateurs ont automatiquement les privilèges des modérateurs"; "screen_room_change_role_moderators_title" = "Modifier les modérateurs"; "screen_room_change_role_unsaved_changes_description" = "Vous avez des modifications non-enregistrées."; -"screen_room_change_role_unsaved_changes_title" = "Enregistrer les modifications?"; "screen_room_details_add_topic_title" = "Ajouter un sujet"; "screen_room_details_already_a_member" = "Déjà membre"; "screen_room_details_already_invited" = "Déjà invité(e)"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Échec de la désactivation de la mise en sourdine de ce salon, veuillez réessayer."; "screen_room_details_notification_mode_custom" = "Personnalisé"; "screen_room_details_notification_mode_default" = "Défaut"; -"screen_room_details_notification_title" = "Notifications"; "screen_room_details_share_room_title" = "Partager le salon"; "screen_room_details_title" = "Informations du salon"; "screen_room_details_updating_room" = "Mise à jour du salon…"; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Bannissement de %1$@"; "screen_room_member_list_manage_member_ban" = "Retirer et bannir ce membre"; "screen_room_member_list_manage_member_remove" = "Retirer le membre du salon"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Retirer et bannir le membre"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Retirer le membre uniquement"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Retirer le membre et interdire l'adhésion à l'avenir ?"; "screen_room_member_list_manage_member_unban_action" = "Débannir"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Marquer comme lu"; "screen_roomlist_mark_as_unread" = "Marquer comme non lu"; "screen_roomlist_room_directory_button_title" = "Parcourir tous les salons"; -"screen_server_confirmation_change_server" = "Changer de fournisseur de compte"; "screen_server_confirmation_message_login_element_dot_io" = "Un serveur privé pour les employés d’Element."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix est un réseau ouvert pour une communication sécurisée et décentralisée."; "screen_server_confirmation_message_register" = "C’est ici que vos conversations seront enregistrées, comme vous le feriez avec un fournisseur de messagerie pour conserver vos e-mails."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Vous êtes en train de vous déconnecter de votre dernière session. Si vous vous déconnectez maintenant, vous perdrez l’accès à l’historique de vos discussions chiffrées."; "screen_signout_key_backup_disabled_title" = "Vous avez désactivé la sauvegarde"; "screen_signout_key_backup_offline_subtitle" = "Vos clés étaient en cours de sauvegarde lorsque vous avez perdu la connexion au réseau. Il faudrait rétablir cette connexion afin de pouvoir terminer la sauvegarde avant de vous déconnecter."; -"screen_signout_key_backup_offline_title" = "Vos clés sont en cours de sauvegarde"; "screen_signout_key_backup_ongoing_subtitle" = "Veuillez attendre que cela se termine avant de vous déconnecter."; "screen_signout_key_backup_ongoing_title" = "Vos clés sont en cours de sauvegarde"; "screen_signout_recovery_disabled_subtitle" = "Vous êtes sur le point de vous déconnecter de votre dernier appareil. Si vous le faites maintenant, vous perdrez l’accès à l’historique de vos messages."; "screen_signout_recovery_disabled_title" = "La récupération n’est pas configurée."; "screen_signout_save_recovery_key_subtitle" = "Vous êtes sur le point de vous déconnecter de votre dernière session. Si vous le faites maintenant, vous perdrez l’accès à l’historique de vos discussions chiffrées."; -"screen_signout_save_recovery_key_title" = "Avez-vous sauvegardé votre clé de récupération?"; "screen_start_chat_error_starting_chat" = "Une erreur s’est produite lors de la tentative de création de la discussion"; "screen_view_location_title" = "Position"; "screen_welcome_bullet_1" = "Les appels, les sondages, les recherches et plus encore seront ajoutés plus tard cette année."; @@ -920,7 +908,6 @@ "test_language_identifier" = "fr"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Dépannage"; -"troubleshoot_notifications_entry_point_title" = "Résoudre les problèmes liés aux notifications"; "troubleshoot_notifications_screen_action" = "Exécuter les tests"; "troubleshoot_notifications_screen_action_again" = "Relancer les tests"; "troubleshoot_notifications_screen_failure" = "Certains tests ont échoué. Veuillez vérifier les détails."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Vérifier qu’au moins un distributeur UnifiedPush est disponible."; "troubleshoot_notifications_test_unified_push_failure" = "Aucun distributeur UnifiedPush n'a été trouvé."; "troubleshoot_notifications_test_unified_push_title" = "Vérifier UnifiedPush"; +"a11y_poll" = "Sondage"; "dialog_title_error" = "Erreur"; "dialog_title_success" = "Succès"; "notification_fallback_content" = "Notification"; "notification_invitation_action_join" = "Rejoindre"; +"notification_invitation_action_reject" = "Rejeter"; "notification_room_action_mark_as_read" = "Marquer comme lu"; "notification_room_action_quick_reply" = "Réponse rapide"; +"screen_pinned_timeline_screen_title_empty" = "Messages épinglés"; "screen_room_mentions_at_room_title" = "Tout le monde"; +"screen_account_provider_change" = "Changer de fournisseur de compte"; "screen_account_provider_signin_subtitle" = "C’est ici que vos conversations seront enregistrées, comme vous le feriez avec un fournisseur de messagerie pour conserver vos e-mails."; "screen_account_provider_signup_subtitle" = "C’est ici que vos conversations seront enregistrées, comme vous le feriez avec un fournisseur de messagerie pour conserver vos e-mails."; "screen_analytics_settings_help_us_improve" = "Partagez des données d’utilisation anonymes pour nous aider à identifier les problèmes."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Vous pourrez à nouveau voir tous ses messages."; "screen_blocked_users_unblock_alert_title" = "Débloquer l’utilisateur"; "screen_bug_report_rash_logs_alert_title" = "%1$@ s’est arrêté la dernière fois qu’il a été utilisé. Souhaitez-vous partager un rapport d’incident avec nous ?"; +"screen_chat_backup_recovery_action_confirm" = "Utiliser la clé de récupération"; +"screen_create_poll_cancel_confirmation_content_ios" = "Vos modifications ne seront pas enregistrées"; "screen_create_room_add_people_title" = "Inviter des amis"; "screen_create_room_room_name_label" = "Nom du salon"; "screen_create_room_title" = "Créer un salon"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Modifier le sondage"; "screen_identity_use_another_device" = "Utiliser une autre session"; "screen_login_subtitle" = "Matrix est un réseau ouvert pour une communication sécurisée et décentralisée."; +"screen_notification_settings_mentions_section_title" = "Mentions"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Essayer à nouveau"; +"screen_recovery_key_confirm_title" = "Confirmer votre clé de récupération"; "screen_report_content_block_user" = "Bloquer l’utilisateur"; +"screen_reset_encryption_password_placeholder" = "Saisissez la clé ici…"; "screen_room_attachment_source_camera_photo" = "Prendre une photo"; "screen_room_change_permissions_everyone" = "Tout le monde"; "screen_room_change_permissions_member_moderation" = "Administration des membres"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Administrateurs"; "screen_room_change_role_section_moderators" = "Modérateurs"; "screen_room_change_role_section_users" = "Membres"; +"screen_room_change_role_unsaved_changes_title" = "Enregistrer les changements?"; "screen_room_details_invite_people_title" = "Inviter des amis"; "screen_room_details_leave_conversation_title" = "Quitter la discussion"; "screen_room_details_leave_room_title" = "Quitter le salon"; +"screen_room_details_notification_title" = "Notifications"; "screen_room_details_roles_and_permissions" = "Rôles et autorisations"; "screen_room_details_room_name_label" = "Nom du salon"; "screen_room_details_security_title" = "Sécurité"; "screen_room_details_topic_title" = "Sujet"; "screen_room_error_failed_processing_media" = "Échec du traitement des médias à télécharger, veuillez réessayer."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Retirer et bannir ce membre"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Mentions et mots clés uniquement"; "screen_roomlist_filter_people" = "Personnes"; +"screen_server_confirmation_change_server" = "Changer de fournisseur de compte"; "screen_signout_confirmation_dialog_submit" = "Se déconnecter"; "screen_signout_confirmation_dialog_title" = "Se déconnecter"; +"screen_signout_key_backup_offline_title" = "Vos clés sont en cours de sauvegarde"; "screen_signout_preference_item" = "Se déconnecter"; +"screen_signout_save_recovery_key_title" = "Avez-vous sauvegardé votre clé de récupération?"; +"troubleshoot_notifications_entry_point_title" = "Dépanner les notifications"; diff --git a/ElementX/Resources/Localizations/hu.lproj/Localizable.strings b/ElementX/Resources/Localizations/hu.lproj/Localizable.strings index 67524fccb6..1e7a98fafa 100644 --- a/ElementX/Resources/Localizations/hu.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/hu.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Szüneteltetés"; "a11y_pin_field" = "PIN-mező"; "a11y_play" = "Lejátszás"; -"a11y_poll" = "Szavazás"; "a11y_poll_end" = "Befejezett szavazás"; "a11y_react_with" = "Reagálás a következővel: %1$@"; "a11y_react_with_other_emojis" = "Reagálás más emodzsikkal"; @@ -41,6 +40,7 @@ "action_create" = "Létrehozás"; "action_create_a_room" = "Szoba létrehozása"; "action_deactivate" = "Deaktiválás"; +"action_deactivate_account" = "Fiók deaktiválása"; "action_decline" = "Elutasítás"; "action_delete_poll" = "Szavazás törlése"; "action_disable" = "Letiltás"; @@ -64,6 +64,7 @@ "action_leave" = "Elhagyás"; "action_leave_conversation" = "Beszélgetés elhagyása"; "action_leave_room" = "Szoba elhagyása"; +"action_load_more" = "Továbbiak betöltése"; "action_manage_account" = "Fiók kezelése"; "action_manage_devices" = "Eszközök kezelése"; "action_message" = "Üzenet"; @@ -93,6 +94,7 @@ "action_send_message" = "Üzenet küldése"; "action_share" = "Megosztás"; "action_share_link" = "Hivatkozás megosztása"; +"action_show" = "Megjelenítés"; "action_sign_in_again" = "Jelentkezzen be újra"; "action_signout" = "Kijelentkezés"; "action_signout_anyway" = "Kijelentkezés mindenképp"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "Megtekintés az idővonalon"; "action_view_source" = "Forrás megtekintése"; "action_yes" = "Igen"; -"action.load_more" = "Továbbiak betöltése"; -"action_deactivate_account" = "Fiók deaktiválása"; "banner_migrate_to_native_sliding_sync_action" = "Kijelentkezés és frissítés"; "banner_migrate_to_native_sliding_sync_description" = "A kiszolgálója mostantól egy új, gyorsabb protokollt támogat. A frissítéshez jelentkezzen ki, majd jelentkezzen be újra. Ha ezt most megteszi, elkerülheti a kényszerített kijelentkeztetést a régi protokollt eltávolításakor."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "A Matrix-kiszolgáló már nem támogatja a régi protokollt. Az alkalmazás további használatához jelentkezzen ki és be."; @@ -162,6 +162,7 @@ "common_modern" = "Modern"; "common_mute" = "Némítás"; "common_no_results" = "Nincs találat"; +"common_no_room_name" = "Nincs szobanév"; "common_offline" = "Kapcsolat nélkül"; "common_optic_id_ios" = "Optic ID"; "common_or" = "vagy"; @@ -170,6 +171,8 @@ "common_permalink" = "Állandó hivatkozás"; "common_permission" = "Engedély"; "common_please_wait" = "Kis türelmet…"; +"common_poll_end_confirmation" = "Biztos, hogy befejezi ezt a szavazást?"; +"common_poll_summary" = "Szavazás: %1$@"; "common_poll_total_votes" = "Összes szavazat: %1$@"; "common_poll_undisclosed_text" = "Az eredmények a szavazás befejezése után jelennek meg"; "common_privacy_policy" = "Adatvédelmi nyilatkozat"; @@ -200,6 +203,7 @@ "common_settings" = "Beállítások"; "common_shared_location" = "Megosztott tartózkodási hely"; "common_signing_out" = "Kijelentkezés"; +"common_something_went_wrong" = "Valamilyen hiba történt"; "common_starting_chat" = "Csevegés megkezdése…"; "common_sticker" = "Matrica"; "common_success" = "Sikeres"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "Miről szól ez a szoba?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Nem lehet visszafejteni"; +"common_unable_to_decrypt_no_access" = "Nincs hozzáférése ehhez az üzenethez"; "common_unable_to_invite_message" = "Nem sikerült meghívót küldeni egy vagy több felhasználónak."; "common_unable_to_invite_title" = "Nem sikerült elküldeni a meghívót (meghívókat)"; "common_unlock" = "Feloldás"; @@ -221,24 +226,21 @@ "common_username" = "Felhasználónév"; "common_verification_cancelled" = "Az ellenőrzés megszakítva"; "common_verification_complete" = "Az ellenőrzés befejeződött"; +"common_verify_device" = "Eszköz ellenőrzése"; "common_video" = "Videó"; "common_voice_message" = "Hangüzenet"; "common_waiting" = "Várakozás…"; "common_waiting_for_decryption_key" = "Várakozás a visszafejtési kulcsra"; +"common.copied_to_clipboard" = "A vágólapra másolva"; "common.do_not_show_this_again" = "Ne jelenjen meg többé"; "common.open_source_licenses" = "Nyílt forráskódú licencek"; "common.pinned" = "Kitűzve"; "common.send_to" = "Címzett"; -"common.you" = "You"; -"common_no_room_name" = "Nincs szobanév"; -"common_poll_end_confirmation" = "Biztos, hogy befejezi ezt a szavazást?"; -"common_poll_summary" = "Szavazás: %1$@"; -"common_something_went_wrong" = "Valamilyen hiba történt"; -"common_unable_to_decrypt_no_access" = "Nincs hozzáférése ehhez az üzenethez"; -"common_verify_device" = "Eszköz ellenőrzése"; +"common.you" = "Ön"; "confirm_recovery_key_banner_message" = "A csevegés biztonsági mentése nincs szinkronban. Meg kell erősítenie a helyreállítási kulcsát, hogy továbbra is hozzáférjen a csevegés biztonsági mentéséhez."; "confirm_recovery_key_banner_title" = "Helyreállítási kulcs megerősítése"; "crash_detection_dialog_content" = "Az %1$@ összeomlott a legutóbbi használata óta. Megosztja velünk az összeomlás-jelentést?"; +"crypto_identity_change_pin_violation" = "Úgy tűnik, hogy %1$@ személyazonossága megváltozott. %2$@"; "dialog_permission_camera" = "Hogy az alkalmazás használhassa a kamerát, adja meg az engedélyt a rendszerbeállításokban."; "dialog_permission_generic" = "Adja meg az engedélyt a rendszerbeállításokban."; "dialog_permission_location_description_ios" = "Adjon hozzáférést a Beállítások -> Hely menüpontban."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "Csendes értesítések"; "notification_incoming_call" = "Bejövő hívás"; "notification_inline_reply_failed" = "** Nem sikerült elküldeni – nyissa meg a szobát"; -"notification_invitation_action_reject" = "Elutasítás"; "notification_invite_body" = "Meghívta, hogy csevegjen"; "notification_invite_body_with_sender" = "%1$@ meghívta egy csevegésre"; "notification_mentioned_you_body" = "Megemlítette Önt: %1$@"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "Érvénytelen webcím, győződjön meg arról, hogy szerepel-e benne a protokoll (http/https), és hogy helyes-e a cím."; "screen_pinned_timeline_empty_state_description" = "Nyomjon hosszan az üzenetre, és válassza a „%1$@” lehetőséget, hogy itt szerepeljen."; "screen_pinned_timeline_empty_state_headline" = "Tűzze ki a fontos üzeneteket, hogy könnyen felfedezhetők legyenek"; -"screen_pinned_timeline_screen_title_empty" = "Kitűzött üzenetek"; "screen_reset_encryption_password_error" = "Ismeretlen hiba történt. Ellenőrizze, hogy a fiókja jelszava helyes-e, és próbálja meg újra."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Ellenőrzés visszavonása és elküldés"; "screen_resolve_send_failure_changed_identity_subtitle" = "Visszavonhatja az ellenőrzést, és ennek ellenére elküldheti ezt az üzenetet, vagy egyelőre törölheti, és %1$@ újbóli ellenőrzése után újra megpróbálhatja."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Az üzenet nem lett elküldve, mert %1$@ ellenőrzött személyazonossága megváltozott."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Az üzenet nem lett elküldve, mert %1$@ nem ellenőrizte az összes eszközét."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Az üzenet nem lett elküldve, mert egy vagy több eszközét nem ellenőrizte."; -"screen_account_provider_change" = "Fiókszolgáltató módosítása"; "screen_account_provider_form_hint" = "Matrix-kiszolgáló webcíme"; "screen_account_provider_form_notice" = "Adjon meg egy keresési kifejezést vagy egy tartománycímet."; "screen_account_provider_form_subtitle" = "Keresés egy cégre, közösségre vagy privát kiszolgálóra."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "A biztonsági mentés biztosítja, hogy ne veszítse el az üzenetelőzményeit. %1$@."; "screen_chat_backup_key_backup_title" = "Biztonsági mentés"; "screen_chat_backup_recovery_action_change" = "Helyreállítási kulcs módosítása"; -"screen_chat_backup_recovery_action_confirm" = "Helyreállítási kulcs megerősítése"; "screen_chat_backup_recovery_action_confirm_description" = "A csevegéselőzményei nincsenek szinkronban."; "screen_chat_backup_recovery_action_setup" = "Helyreállítás beállítása"; "screen_chat_backup_recovery_action_setup_description" = "Szerezzen hozzáférést a titkosított üzeneteihez, ha elvesztette az összes eszközét, vagy ha mindenütt kijelentkezett az %1$@ből."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Eredmények megjelenítése csak a szavazás befejezése után"; "screen_create_poll_anonymous_headline" = "Szavazatok elrejtése"; "screen_create_poll_answer_hint" = "%1$d. lehetőség"; -"screen_create_poll_cancel_confirmation_content_ios" = "A változtatások nem lesznek mentve"; "screen_create_poll_cancel_confirmation_title_ios" = "Szavazás elvetése"; "screen_create_poll_question_desc" = "Kérdés vagy téma"; "screen_create_poll_question_hint" = "Miről szól ez a szavazás?"; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Csoportos csevegések"; "screen_notification_settings_invite_for_me_label" = "Meghívók"; "screen_notification_settings_mentions_only_disclaimer" = "A Matrix-kiszolgálója nem támogatja ezt a beállítást a titkosított szobákban, előfordulhat, hogy egyes szobákban nem kap értesítést."; -"screen_notification_settings_mentions_section_title" = "Említések"; "screen_notification_settings_mode_all" = "Összes"; "screen_notification_settings_mode_mentions" = "Említések"; "screen_notification_settings_notification_section_title" = "Értesítés ezekről:"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Megadás…"; "screen_recovery_key_confirm_lost_recovery_key" = "Elvesztette a helyreállítási kulcsát?"; "screen_recovery_key_confirm_success" = "Helyreállítási kulcs megerősítve"; -"screen_recovery_key_confirm_title" = "Adja meg a helyreállítási kulcsát"; "screen_recovery_key_copied_to_clipboard" = "Helyreállítási kulcs másolva"; "screen_recovery_key_generating_key" = "Előállítás…"; "screen_recovery_key_save_action" = "Helyreállítási kulcs mentése"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Igen, visszaállítás most"; "screen_reset_encryption_confirmation_alert_subtitle" = "Ez a folyamat visszafordíthatatlan."; "screen_reset_encryption_confirmation_alert_title" = "Biztos, hogy visszaállítja a titkosítást?"; -"screen_reset_encryption_password_placeholder" = "Adja meg…"; "screen_reset_encryption_password_subtitle" = "Erősítse meg, hogy vissza szeretné állítani a titkosítást."; "screen_reset_encryption_password_title" = "A folytatáshoz adja meg fiókja jelszavát"; "screen_reset_identity_confirmation_subtitle" = "Arra készül, hogy belépjen a(z) %1$@ fiókjába, hogy visszaállítsa a személyazonosságát. Ezután vissza fog térni az alkalmazásba."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Az adminisztrátorok automatikusan moderátori jogosultságokkal rendelkeznek"; "screen_room_change_role_moderators_title" = "Moderátorok szerkesztése"; "screen_room_change_role_unsaved_changes_description" = "Mentetlen módosításai vannak."; -"screen_room_change_role_unsaved_changes_title" = "Menti a változtatásokat?"; "screen_room_details_add_topic_title" = "Téma hozzáadása"; "screen_room_details_already_a_member" = "Már tag"; "screen_room_details_already_invited" = "Már meghívták"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Nem sikerült feloldani a szoba némítását, próbálja újra."; "screen_room_details_notification_mode_custom" = "Egyéni"; "screen_room_details_notification_mode_default" = "Alapértelmezett"; -"screen_room_details_notification_title" = "Értesítések"; "screen_room_details_share_room_title" = "Szoba megosztása"; "screen_room_details_title" = "Szobainformációk"; "screen_room_details_updating_room" = "Szoba frissítése…"; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "%1$@ kitiltása"; "screen_room_member_list_manage_member_ban" = "Eltávolítás és a tag kitiltása"; "screen_room_member_list_manage_member_remove" = "Eltávolítás a szobából"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Tag eltávolítása és kitiltása"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Csak a tag eltávolítása"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Eltávolítja a tagot, és megtiltja a jövőbeni csatlakozást?"; "screen_room_member_list_manage_member_unban_action" = "Tiltás feloldása"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Megjelölés olvasottként"; "screen_roomlist_mark_as_unread" = "Megjelölés olvasatlanként"; "screen_roomlist_room_directory_button_title" = "Összes szoba böngészése"; -"screen_server_confirmation_change_server" = "Fiókszolgáltató módosítása"; "screen_server_confirmation_message_login_element_dot_io" = "Egy privát kiszolgáló az Element alkalmazottai számára."; "screen_server_confirmation_message_login_matrix_dot_org" = "A Matrix egy nyitott hálózat a biztonságos, decentralizált kommunikációhoz."; "screen_server_confirmation_message_register" = "Itt lesznek a beszélgetései – ahogyan egy e-mail-szolgáltatást is használna a levelei kezeléséhez."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Arra készül, hogy kijelentkezzen az utolsó munkamenetéből is. Ha most kijelentkezik, akkor elveszti a hozzáférését a titkosított üzeneteihez."; "screen_signout_key_backup_disabled_title" = "Kikapcsolta a biztonsági mentést"; "screen_signout_key_backup_offline_subtitle" = "A kulcsai mentése során bontotta a kapcsolatot. Kapcsolódjon újra, hogy a kulcsai továbbra is mentésre kerüljenek mielőtt kijelentkezik."; -"screen_signout_key_backup_offline_title" = "A kulcsai mentése még folyamatban van"; "screen_signout_key_backup_ongoing_subtitle" = "Kijelentkezés előtt várja meg a befejezését."; "screen_signout_key_backup_ongoing_title" = "A kulcsai mentése még folyamatban van"; "screen_signout_recovery_disabled_subtitle" = "Arra készül, hogy kijelentkezzen az utolsó munkamenetéből is. Ha most kijelentkezik, akkor elveszti a hozzáférését a titkosított üzeneteihez."; "screen_signout_recovery_disabled_title" = "A helyreállítás nincs beállítva"; "screen_signout_save_recovery_key_subtitle" = "Arra készül, hogy kijelentkezzen az utolsó munkamenetéből is. Ha most kijelentkezik, akkor elveszítheti a hozzáférését a titkosított üzeneteihez."; -"screen_signout_save_recovery_key_title" = "Mentette a helyreállítási kulcsát?"; "screen_start_chat_error_starting_chat" = "Hiba történt a csevegés indításakor"; "screen_view_location_title" = "Hely"; "screen_welcome_bullet_1" = "A hívások, szavazások, keresések és egyebek az év további részében kerülnek hozzáadásra."; @@ -920,7 +908,6 @@ "test_language_identifier" = "hu"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Hibaelhárítás"; -"troubleshoot_notifications_entry_point_title" = "Értesítések hibaelhárítása"; "troubleshoot_notifications_screen_action" = "Tesztek futtatása"; "troubleshoot_notifications_screen_action_again" = "Tesztek újbóli futtatása"; "troubleshoot_notifications_screen_failure" = "Egyes tesztek sikertelenek voltak. Ellenőrizze a részleteket."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Győződjön meg arról, hogy a UnifiedPush forgalmazói elérhetők."; "troubleshoot_notifications_test_unified_push_failure" = "Nem található forgalmazó a leküldéses értesítésekhez."; "troubleshoot_notifications_test_unified_push_title" = "Ellenőrizze a UnifiedPush szolgáltatást"; +"a11y_poll" = "Szavazás"; "dialog_title_error" = "Hiba"; "dialog_title_success" = "Sikeres"; "notification_fallback_content" = "Értesítés"; "notification_invitation_action_join" = "Csatlakozás"; +"notification_invitation_action_reject" = "Elutasítás"; "notification_room_action_mark_as_read" = "Megjelölés olvasottként"; "notification_room_action_quick_reply" = "Gyors válasz"; +"screen_pinned_timeline_screen_title_empty" = "Kitűzött üzenetek"; "screen_room_mentions_at_room_title" = "Mindenki"; +"screen_account_provider_change" = "Fiókszolgáltató módosítása"; "screen_account_provider_signin_subtitle" = "Itt lesznek a beszélgetései – ahogyan egy e-mail-szolgáltatást is használna a levelei kezeléséhez."; "screen_account_provider_signup_subtitle" = "Itt lesznek a beszélgetései – ahogyan egy e-mail-szolgáltatást is használna a levelei kezeléséhez."; "screen_analytics_settings_help_us_improve" = "Anonim használati adatok megosztása a problémák azonosítása érdekében."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Újra láthatja az összes üzenetét."; "screen_blocked_users_unblock_alert_title" = "Felhasználó kitiltásának feloldása"; "screen_bug_report_rash_logs_alert_title" = "Az %1$@ összeomlott a legutóbbi használata óta. Megosztja velünk az összeomlás-jelentést?"; +"screen_chat_backup_recovery_action_confirm" = "Adja meg a helyreállítási kulcsot"; +"screen_create_poll_cancel_confirmation_content_ios" = "A módosításai nem lesznek mentve"; "screen_create_room_add_people_title" = "Ismerősök meghívása"; "screen_create_room_room_name_label" = "Szoba neve"; "screen_create_room_title" = "Szoba létrehozása"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Szavazás szerkesztése"; "screen_identity_use_another_device" = "Másik eszköz használata"; "screen_login_subtitle" = "A Matrix egy nyitott hálózat a biztonságos, decentralizált kommunikációhoz."; +"screen_notification_settings_mentions_section_title" = "Említések"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Próbálja újra"; +"screen_recovery_key_confirm_title" = "Helyreállítási kulcs megerősítése"; "screen_report_content_block_user" = "Felhasználó letiltása"; +"screen_reset_encryption_password_placeholder" = "Megadás…"; "screen_room_attachment_source_camera_photo" = "Fénykép készítése"; "screen_room_change_permissions_everyone" = "Mindenki"; "screen_room_change_permissions_member_moderation" = "Tagok moderálása"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Adminisztrátorok"; "screen_room_change_role_section_moderators" = "Moderátorok"; "screen_room_change_role_section_users" = "Tagok"; +"screen_room_change_role_unsaved_changes_title" = "Menti a módosításokat?"; "screen_room_details_invite_people_title" = "Ismerősök meghívása"; "screen_room_details_leave_conversation_title" = "Beszélgetés elhagyása"; "screen_room_details_leave_room_title" = "Szoba elhagyása"; +"screen_room_details_notification_title" = "Értesítések"; "screen_room_details_roles_and_permissions" = "Szerepkörök és jogosultságok"; "screen_room_details_room_name_label" = "Szoba neve"; "screen_room_details_security_title" = "Biztonság"; "screen_room_details_topic_title" = "Téma"; "screen_room_error_failed_processing_media" = "Nem sikerült feldolgozni a feltöltendő médiát, próbálja újra."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Eltávolítás és a tag kitiltása"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Csak említések és kulcsszavak"; "screen_roomlist_filter_people" = "Emberek"; +"screen_server_confirmation_change_server" = "Fiókszolgáltató módosítása"; "screen_signout_confirmation_dialog_submit" = "Kijelentkezés"; "screen_signout_confirmation_dialog_title" = "Kijelentkezés"; +"screen_signout_key_backup_offline_title" = "A kulcsai mentése még folyamatban van"; "screen_signout_preference_item" = "Kijelentkezés"; +"screen_signout_save_recovery_key_title" = "Mentette a helyreállítási kulcsát?"; +"troubleshoot_notifications_entry_point_title" = "Értesítések hibaelhárítása"; diff --git a/ElementX/Resources/Localizations/id.lproj/Localizable.strings b/ElementX/Resources/Localizations/id.lproj/Localizable.strings index 10770d9c34..c968b46e9e 100644 --- a/ElementX/Resources/Localizations/id.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/id.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Jeda"; "a11y_pin_field" = "Kolom PIN"; "a11y_play" = "Putar"; -"a11y_poll" = "Pemungutan suara"; "a11y_poll_end" = "Pemungutan suara berakhir"; "a11y_react_with" = "Bereaksi dengan %1$@"; "a11y_react_with_other_emojis" = "Reaksi dengan emoji lain"; @@ -41,6 +40,7 @@ "action_create" = "Buat"; "action_create_a_room" = "Buat ruangan"; "action_deactivate" = "Deactivate"; +"action_deactivate_account" = "Deactivate account"; "action_decline" = "Tolak"; "action_delete_poll" = "Hapus pemungutan suara"; "action_disable" = "Nonaktifkan"; @@ -64,6 +64,7 @@ "action_leave" = "Tinggalkan"; "action_leave_conversation" = "Tinggalkan percakapan"; "action_leave_room" = "Tinggalkan ruangan"; +"action_load_more" = "Muat lainnya"; "action_manage_account" = "Kelola akun"; "action_manage_devices" = "Kelola perangkat"; "action_message" = "Kirim pesan"; @@ -93,6 +94,7 @@ "action_send_message" = "Kirim pesan"; "action_share" = "Bagikan"; "action_share_link" = "Bagikan tautan"; +"action_show" = "Show"; "action_sign_in_again" = "Masuk lagi"; "action_signout" = "Keluar dari akun"; "action_signout_anyway" = "Keluar saja"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "View in timeline"; "action_view_source" = "Tampilkan sumber"; "action_yes" = "Ya"; -"action.load_more" = "Muat lainnya"; -"action_deactivate_account" = "Deactivate account"; "banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; @@ -162,6 +162,7 @@ "common_modern" = "Modern"; "common_mute" = "Bisukan"; "common_no_results" = "Tidak ada hasil"; +"common_no_room_name" = "Tidak ada nama ruangan"; "common_offline" = "Luring"; "common_optic_id_ios" = "Optic ID"; "common_or" = "atau"; @@ -170,6 +171,8 @@ "common_permalink" = "Tautan Permanen"; "common_permission" = "Perizinan"; "common_please_wait" = "Mohon tunggu…"; +"common_poll_end_confirmation" = "Apakah Anda yakin ingin mengakhiri pemungutan suara ini?"; +"common_poll_summary" = "Pemungutan suara: %1$@"; "common_poll_total_votes" = "Total suara: %1$@"; "common_poll_undisclosed_text" = "Hasil akan terlihat setelah pemungutan suara berakhir"; "common_privacy_policy" = "Kebijakan privasi"; @@ -200,6 +203,7 @@ "common_settings" = "Pengaturan"; "common_shared_location" = "Lokasi terbagi"; "common_signing_out" = "Mengeluarkan dari akun"; +"common_something_went_wrong" = "Ada yang salah"; "common_starting_chat" = "Memulai obrolan..."; "common_sticker" = "Stiker"; "common_success" = "Berhasil"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "Tentang apa ruangan ini?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Tidak dapat mendekripsi"; +"common_unable_to_decrypt_no_access" = "Anda tidak memiliki akses ke pesan ini"; "common_unable_to_invite_message" = "Undangan tidak dapat dikirim ke satu atau beberapa pengguna."; "common_unable_to_invite_title" = "Tidak dapat mengirim undangan"; "common_unlock" = "Buka kunci"; @@ -221,24 +226,21 @@ "common_username" = "Nama pengguna"; "common_verification_cancelled" = "Verifikasi dibatalkan"; "common_verification_complete" = "Verifikasi selesai"; +"common_verify_device" = "Verifikasi perangkat"; "common_video" = "Video"; "common_voice_message" = "Pesan suara"; "common_waiting" = "Menunggu…"; "common_waiting_for_decryption_key" = "Menunggu pesan ini"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "Jangan tampilkan ini lagi"; "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "Kirim ke"; "common.you" = "You"; -"common_no_room_name" = "Tidak ada nama ruangan"; -"common_poll_end_confirmation" = "Apakah Anda yakin ingin mengakhiri pemungutan suara ini?"; -"common_poll_summary" = "Pemungutan suara: %1$@"; -"common_something_went_wrong" = "Ada yang salah"; -"common_unable_to_decrypt_no_access" = "Anda tidak memiliki akses ke pesan ini"; -"common_verify_device" = "Verifikasi perangkat"; "confirm_recovery_key_banner_message" = "Cadangan percakapan Anda saat ini tidak tersinkron. Anda perlu mengonfirmasi kunci pemulihan Anda untuk tetap memiliki akses ke cadangan percakapan Anda."; "confirm_recovery_key_banner_title" = "Konfirmasi kunci pemulihan Anda"; "crash_detection_dialog_content" = "%1$@ mengalami kemogokan saat terakhir kali digunakan. Apakah Anda ingin berbagi laporan kerusakan dengan kami?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "Supaya aplikasinya dapat menggunakan kamera, berikan izin dalam pengaturan sistem."; "dialog_permission_generic" = "Silakan memberikan izin dalam pengaturan sistem."; "dialog_permission_location_description_ios" = "Berikan akses dalam Pengaturan -> Lokasi."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "Pemberitahuan diam"; "notification_incoming_call" = "Panggilan masuk"; "notification_inline_reply_failed" = "** Gagal mengirim — silakan buka ruangan"; -"notification_invitation_action_reject" = "Tolak"; "notification_invite_body" = "Mengundang Anda untuk mengobrol"; "notification_invite_body_with_sender" = "%1$@ invited you to chat"; "notification_mentioned_you_body" = "Menyebutkan Anda: %1$@"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "URL tidak valid, pastikan Anda menyertakan protokol (http/https) dan alamat yang benar."; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; -"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; "screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "Ubah penyedia akun"; "screen_account_provider_form_hint" = "Alamat homeserver"; "screen_account_provider_form_notice" = "Masukkan istilah pencarian atau alamat domain."; "screen_account_provider_form_subtitle" = "Cari perusahaan, komunitas, atau server pribadi."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "Pencadangan memastikan bahwa Anda tidak akan kehilangan riwayat pesan Anda. %1$@."; "screen_chat_backup_key_backup_title" = "Pencadangan"; "screen_chat_backup_recovery_action_change" = "Ubah kunci pemulihan"; -"screen_chat_backup_recovery_action_confirm" = "Konfirmasi kunci pemulihan"; "screen_chat_backup_recovery_action_confirm_description" = "Pencadangan percakapan Anda saat ini tidak tersinkron."; "screen_chat_backup_recovery_action_setup" = "Siapkan pemulihan"; "screen_chat_backup_recovery_action_setup_description" = "Dapatkan akses ke pesan terenkripsi Anda jika Anda kehilangan semua perangkat Anda atau keluar dari %1$@ di mana pun."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Tampilkan hasil hanya setelah pemungutan suara berakhir"; "screen_create_poll_anonymous_headline" = "Pemungutan suara anonim"; "screen_create_poll_answer_hint" = "Opsi %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Perubahan Anda tidak akan disimpan"; "screen_create_poll_cancel_confirmation_title_ios" = "Batal pemungutan suara"; "screen_create_poll_question_desc" = "Pertanyaan atau topik"; "screen_create_poll_question_hint" = "Tentang apa pemungutan suara ini?"; @@ -480,7 +477,7 @@ "screen_edit_profile_updating_details" = "Memperbarui profil…"; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history"; +"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Obrolan grup"; "screen_notification_settings_invite_for_me_label" = "Undangan"; "screen_notification_settings_mentions_only_disclaimer" = "Homeserver Anda tidak mendukung opsi ini dalam ruangan terenkripsi, Anda mungkin tidak diberi tahu dalam beberapa ruangan."; -"screen_notification_settings_mentions_section_title" = "Sebutan"; "screen_notification_settings_mode_all" = "Semua"; "screen_notification_settings_mode_mentions" = "Sebutan"; "screen_notification_settings_notification_section_title" = "Beri tahu saya tentang"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Masukkan..."; "screen_recovery_key_confirm_lost_recovery_key" = "Kehilangan kunci pemulihan Anda?"; "screen_recovery_key_confirm_success" = "Kunci pemulihan dikonfirmasi"; -"screen_recovery_key_confirm_title" = "Konfirmasi kunci pemulihan Anda"; "screen_recovery_key_copied_to_clipboard" = "Kunci pemulihan disalin"; "screen_recovery_key_generating_key" = "Membuat…"; "screen_recovery_key_save_action" = "Simpan kunci pemulihan"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Yes, reset now"; "screen_reset_encryption_confirmation_alert_subtitle" = "This process is irreversible."; "screen_reset_encryption_confirmation_alert_title" = "Are you sure you want to reset your identity?"; -"screen_reset_encryption_password_placeholder" = "Enter…"; "screen_reset_encryption_password_subtitle" = "Confirm that you want to reset your identity."; "screen_reset_encryption_password_title" = "Enter your account password to continue"; "screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Admin secara otomatis memiliki hak moderator"; "screen_room_change_role_moderators_title" = "Sunting Moderator"; "screen_room_change_role_unsaved_changes_description" = "Anda memiliki perubahan yang belum disimpan."; -"screen_room_change_role_unsaved_changes_title" = "Simpan perubahan?"; "screen_room_details_add_topic_title" = "Tambahkan topik"; "screen_room_details_already_a_member" = "Sudah menjadi anggota"; "screen_room_details_already_invited" = "Sudah diundang"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Gagal membunyikan ruangan ini, silakan coba lagi."; "screen_room_details_notification_mode_custom" = "Khusus"; "screen_room_details_notification_mode_default" = "Bawaan"; -"screen_room_details_notification_title" = "Pemberitahuan"; "screen_room_details_share_room_title" = "Bagikan ruangan"; "screen_room_details_title" = "Info ruangan"; "screen_room_details_updating_room" = "Memperbarui ruangan…"; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Mencekal %1$@"; "screen_room_member_list_manage_member_ban" = "Keluarkan dan cekal anggota"; "screen_room_member_list_manage_member_remove" = "Keluarkan dari ruangan"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Keluarkan dan cekal anggota"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Hanya keluarkan anggota"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Keluarkan pengguna dan cekal pengguna bergabung lagi di masa mendatang?"; "screen_room_member_list_manage_member_unban_action" = "Batalkan pencekalan"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Tandai sebagai dibaca"; "screen_roomlist_mark_as_unread" = "Tandai sebagai belum dibaca"; "screen_roomlist_room_directory_button_title" = "Cari semua ruangan"; -"screen_server_confirmation_change_server" = "Ubah penyedia akun"; "screen_server_confirmation_message_login_element_dot_io" = "Server pribadi untuk karyawan Element."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix adalah jaringan terbuka untuk komunikasi yang aman dan terdesentralisasi."; "screen_server_confirmation_message_register" = "Di sinilah percakapan Anda akan berlangsung — sama seperti Anda menggunakan penyedia surel untuk menyimpan surel Anda."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Anda akan keluar dari sesi terakhir Anda. Jika Anda keluar sekarang, Anda akan kehilangan akses ke pesan terenkripsi Anda."; "screen_signout_key_backup_disabled_title" = "Anda telah menonaktifkan pencadangan"; "screen_signout_key_backup_offline_subtitle" = "Kunci Anda masih dicadangkan saat Anda luring. Sambungkan kembali sehingga kunci Anda dapat dicadangkan sebelum keluar."; -"screen_signout_key_backup_offline_title" = "Kunci Anda masih dicadangkan"; "screen_signout_key_backup_ongoing_subtitle" = "Mohon tunggu hingga proses ini selesai sebelum keluar."; "screen_signout_key_backup_ongoing_title" = "Kunci Anda masih dicadangkan"; "screen_signout_recovery_disabled_subtitle" = "Anda akan keluar dari sesi Anda yang terakhir. Jika Anda keluar sekarang, Anda akan kehilangan akses ke pesan terenkripsi Anda."; "screen_signout_recovery_disabled_title" = "Pemulihan belum disiapkan"; "screen_signout_save_recovery_key_subtitle" = "Anda akan keluar dari sesi terakhir Anda. Jika Anda keluar sekarang, Anda mungkin kehilangan akses ke pesan terenkripsi Anda."; -"screen_signout_save_recovery_key_title" = "Apakah Anda sudah menyimpan kunci pemulihan Anda?"; "screen_start_chat_error_starting_chat" = "Terjadi kesalahan saat mencoba memulai obrolan"; "screen_view_location_title" = "Lokasi"; "screen_welcome_bullet_1" = "Panggilan, pemungutan suara, pencarian, dan lainnya akan ditambahkan di tahun ini."; @@ -920,7 +908,6 @@ "test_language_identifier" = "id"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Pemecahan masalah"; -"troubleshoot_notifications_entry_point_title" = "Pecahkan masalah notifikasi"; "troubleshoot_notifications_screen_action" = "Jalankan tes"; "troubleshoot_notifications_screen_action_again" = "Jalankan tes lagi"; "troubleshoot_notifications_screen_failure" = "Beberapa tes gagal. Silakan periksa detailnya."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Pastikan distributor UnifiedPush tersedia."; "troubleshoot_notifications_test_unified_push_failure" = "Tidak ada distributor notifikasi dorongan yang ditemukan."; "troubleshoot_notifications_test_unified_push_title" = "Periksa UnifiedPush"; +"a11y_poll" = "Pemungutan suara"; "dialog_title_error" = "Eror"; "dialog_title_success" = "Berhasil"; "notification_fallback_content" = "Notifikasi"; "notification_invitation_action_join" = "Gabung"; +"notification_invitation_action_reject" = "Tolak"; "notification_room_action_mark_as_read" = "Tandai sebagai dibaca"; "notification_room_action_quick_reply" = "Balas cepat"; +"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_room_mentions_at_room_title" = "Semua orang"; +"screen_account_provider_change" = "Ubah penyedia akun"; "screen_account_provider_signin_subtitle" = "Di sinilah percakapan Anda akan berlangsung — sama seperti Anda menggunakan penyedia surel untuk menyimpan surel Anda."; "screen_account_provider_signup_subtitle" = "Di sinilah percakapan Anda akan berlangsung — sama seperti Anda menggunakan penyedia surel untuk menyimpan surel Anda."; "screen_analytics_settings_help_us_improve" = "Bagikan data penggunaan anonim untuk membantu kami mengidentifikasi masalah."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Anda akan dapat melihat semua pesan dari mereka lagi."; "screen_blocked_users_unblock_alert_title" = "Buka blokir pengguna"; "screen_bug_report_rash_logs_alert_title" = "%1$@ mengalami kemogokan saat terakhir kali digunakan. Apakah Anda ingin berbagi laporan kerusakan dengan kami?"; +"screen_chat_backup_recovery_action_confirm" = "Masukkan kunci pemulihan"; +"screen_create_poll_cancel_confirmation_content_ios" = "Perubahan Anda tidak akan disimpan"; "screen_create_room_add_people_title" = "Undang orang-orang"; "screen_create_room_room_name_label" = "Nama ruangan"; "screen_create_room_title" = "Buat ruangan"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Sunting pemungutan suara"; "screen_identity_use_another_device" = "Gunakan perangkat lain"; "screen_login_subtitle" = "Matrix adalah jaringan terbuka untuk komunikasi yang aman dan terdesentralisasi."; +"screen_notification_settings_mentions_section_title" = "Sebutan"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Coba lagi"; +"screen_recovery_key_confirm_title" = "Konfirmasi kunci pemulihan Anda"; "screen_report_content_block_user" = "Blokir pengguna"; +"screen_reset_encryption_password_placeholder" = "Masukkan..."; "screen_room_attachment_source_camera_photo" = "Ambil foto"; "screen_room_change_permissions_everyone" = "Semua orang"; "screen_room_change_permissions_member_moderation" = "Moderasi anggota"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Admin"; "screen_room_change_role_section_moderators" = "Moderator"; "screen_room_change_role_section_users" = "Anggota"; +"screen_room_change_role_unsaved_changes_title" = "Simpan perubahan?"; "screen_room_details_invite_people_title" = "Undang orang-orang"; "screen_room_details_leave_conversation_title" = "Tinggalkan percakapan"; "screen_room_details_leave_room_title" = "Tinggalkan ruangan"; +"screen_room_details_notification_title" = "Notifikasi"; "screen_room_details_roles_and_permissions" = "Peran dan perizinan"; "screen_room_details_room_name_label" = "Nama ruangan"; "screen_room_details_security_title" = "Keamanan"; "screen_room_details_topic_title" = "Topik"; "screen_room_error_failed_processing_media" = "Gagal memproses media untuk diunggah, silakan coba lagi."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Keluarkan dan cekal anggota"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Sebutan dan Kata Kunci saja"; "screen_roomlist_filter_people" = "Orang"; +"screen_server_confirmation_change_server" = "Ubah penyedia akun"; "screen_signout_confirmation_dialog_submit" = "Keluar dari akun"; "screen_signout_confirmation_dialog_title" = "Keluar dari akun"; +"screen_signout_key_backup_offline_title" = "Kunci Anda masih dicadangkan"; "screen_signout_preference_item" = "Keluar dari akun"; +"screen_signout_save_recovery_key_title" = "Apakah Anda sudah menyimpan kunci pemulihan Anda?"; +"troubleshoot_notifications_entry_point_title" = "Pecahkan masalah notifikasi"; diff --git a/ElementX/Resources/Localizations/it.lproj/Localizable.strings b/ElementX/Resources/Localizations/it.lproj/Localizable.strings index 2a7b8e2258..ad7756355f 100644 --- a/ElementX/Resources/Localizations/it.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/it.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Pausa"; "a11y_pin_field" = "Campo del PIN"; "a11y_play" = "Riproduci"; -"a11y_poll" = "Sondaggio"; "a11y_poll_end" = "Sondaggio terminato"; "a11y_react_with" = "Reagisci con %1$@"; "a11y_react_with_other_emojis" = "Reagisci con altri emoji"; @@ -27,20 +26,21 @@ "action_back" = "Indietro"; "action_call" = "Chiama"; "action_cancel" = "Annulla"; -"action_cancel_for_now" = "Cancel for now"; +"action_cancel_for_now" = "Annulla per ora"; "action_choose_photo" = "Scegli foto"; "action_clear" = "Cancella"; "action_close" = "Chiudi"; "action_complete_verification" = "Completa verifica"; "action_confirm" = "Conferma"; -"action_confirm_password" = "Confirm password"; +"action_confirm_password" = "Conferma password"; "action_continue" = "Continua"; "action_copy" = "Copia"; "action_copy_link" = "Copia collegamento"; "action_copy_link_to_message" = "Copia collegamento al messaggio"; "action_create" = "Crea"; "action_create_a_room" = "Crea una stanza"; -"action_deactivate" = "Deactivate"; +"action_deactivate" = "Disattiva"; +"action_deactivate_account" = "Disattiva account"; "action_decline" = "Rifiuta"; "action_delete_poll" = "Elimina sondaggio"; "action_disable" = "Disabilita"; @@ -64,6 +64,7 @@ "action_leave" = "Esci"; "action_leave_conversation" = "Abbandona la conversazione"; "action_leave_room" = "Esci dalla stanza"; +"action_load_more" = "Carica altro"; "action_manage_account" = "Gestisci account"; "action_manage_devices" = "Gestisci dispositivi"; "action_message" = "Invia messaggio"; @@ -93,6 +94,7 @@ "action_send_message" = "Invia messaggio"; "action_share" = "Condividi"; "action_share_link" = "Condividi collegamento"; +"action_show" = "Show"; "action_sign_in_again" = "Accedi di nuovo"; "action_signout" = "Disconnetti"; "action_signout_anyway" = "Disconnetti comunque"; @@ -105,17 +107,15 @@ "action_tap_for_options" = "Tocca per le opzioni"; "action_try_again" = "Riprova"; "action_unpin" = "Rimuovi dai fissati"; -"action_view_in_timeline" = "View in timeline"; +"action_view_in_timeline" = "Visualizza nella conversazione"; "action_view_source" = "Vedi codice sorgente"; "action_yes" = "Sì"; -"action.load_more" = "Carica altro"; -"action_deactivate_account" = "Deactivate account"; -"banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; -"banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; -"banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; -"banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; -"banner.set_up_recovery.title" = "Set up recovery"; +"banner_migrate_to_native_sliding_sync_action" = "Esci e aggiorna"; +"banner_migrate_to_native_sliding_sync_description" = "Il tuo server ora supporta un nuovo protocollo più veloce. Esci e rientra per effettuare l'aggiornamento. Se lo fai ora, eviterai una disconnessione forzata quando il vecchio protocollo verrà rimosso in seguito."; +"banner_migrate_to_native_sliding_sync_force_logout_title" = "Il tuo homeserver non supporta più il vecchio protocollo. Esci e rientra per continuare a usare l'app."; +"banner_migrate_to_native_sliding_sync_title" = "Aggiornamento disponibile"; +"banner.set_up_recovery.content" = "Genera una nuova chiave di recupero che può essere usata per ripristinare la cronologia dei messaggi crittografati nel caso in cui tu perda l'accesso ai tuoi dispositivi."; +"banner.set_up_recovery.title" = "Configura il ripristino"; "common_about" = "Informazioni"; "common_acceptable_use_policy" = "Regole sull'utilizzo consentito"; "common_advanced_settings" = "Impostazioni avanzate"; @@ -162,6 +162,7 @@ "common_modern" = "Moderno"; "common_mute" = "Silenzia"; "common_no_results" = "Nessun risultato"; +"common_no_room_name" = "Nessun nome della stanza"; "common_offline" = "Non in linea"; "common_optic_id_ios" = "Optic ID"; "common_or" = "o"; @@ -170,6 +171,8 @@ "common_permalink" = "Collegamento permanente"; "common_permission" = "Autorizzazione"; "common_please_wait" = "Attendere prego..."; +"common_poll_end_confirmation" = "Vuoi davvero terminare questo sondaggio?"; +"common_poll_summary" = "Sondaggio: %1$@"; "common_poll_total_votes" = "Voti totali: %1$@"; "common_poll_undisclosed_text" = "I risultati verranno mostrati al termine del sondaggio"; "common_privacy_policy" = "Informativa sulla privacy"; @@ -200,6 +203,7 @@ "common_settings" = "Impostazioni"; "common_shared_location" = "Posizione condivisa"; "common_signing_out" = "Disconnessione"; +"common_something_went_wrong" = "Qualcosa è andato storto"; "common_starting_chat" = "Avvio della conversazione..."; "common_sticker" = "Adesivo"; "common_success" = "Operazione riuscita"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "Di cosa parla questa stanza?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Impossibile decrittografare"; +"common_unable_to_decrypt_no_access" = "Non hai accesso a questo messaggio"; "common_unable_to_invite_message" = "Non è stato possibile spedire inviti a uno o più utenti."; "common_unable_to_invite_title" = "Impossibile inviare inviti"; "common_unlock" = "Sblocca"; @@ -221,24 +226,21 @@ "common_username" = "Nome utente"; "common_verification_cancelled" = "Verifica annullata"; "common_verification_complete" = "Verifica completata"; +"common_verify_device" = "Verifica dispositivo"; "common_video" = "Video"; "common_voice_message" = "Messaggio vocale"; "common_waiting" = "In attesa…"; "common_waiting_for_decryption_key" = "In attesa del messaggio"; +"common.copied_to_clipboard" = "Copiato negli appunti"; "common.do_not_show_this_again" = "Non mostrarlo più"; "common.open_source_licenses" = "Licenze open source"; -"common.pinned" = "Pinned"; +"common.pinned" = "Fissato"; "common.send_to" = "Invia a"; -"common.you" = "You"; -"common_no_room_name" = "Nessun nome della stanza"; -"common_poll_end_confirmation" = "Vuoi davvero terminare questo sondaggio?"; -"common_poll_summary" = "Sondaggio: %1$@"; -"common_something_went_wrong" = "Qualcosa è andato storto"; -"common_unable_to_decrypt_no_access" = "Non hai accesso a questo messaggio"; -"common_verify_device" = "Verifica dispositivo"; +"common.you" = "Tu"; "confirm_recovery_key_banner_message" = "Il backup della chat non è attualmente sincronizzato. Devi confermare la chiave di recupero per mantenere l'accesso al backup della chat."; "confirm_recovery_key_banner_title" = "Inserisci la chiave di recupero"; "crash_detection_dialog_content" = "%1$@ si è chiuso inaspettatamente l'ultima volta che è stato usato. Vuoi condividere con noi un rapporto sull'arresto anomalo?"; +"crypto_identity_change_pin_violation" = "L'identità di %1$@ sembra essere cambiata. %2$@"; "dialog_permission_camera" = "Per permettere all'applicazione di usare la fotocamera, concedi l'autorizzazione nelle impostazioni di sistema."; "dialog_permission_generic" = "Concedi l'autorizzazione nelle impostazioni di sistema."; "dialog_permission_location_description_ios" = "Concedi l'accesso in Impostazioni -> Condividi la mia posizione."; @@ -259,7 +261,7 @@ "emoji_picker_category_people" = "Faccine & Persone"; "emoji_picker_category_places" = "Viaggi & Luoghi"; "emoji_picker_category_symbols" = "Simboli"; -"error_account_creation_not_possible" = "Your homeserver needs to be upgraded to support Matrix Authentication Service and account creation."; +"error_account_creation_not_possible" = "Il tuo homeserver deve essere aggiornato per supportare il Matrix Authentication Service e la creazione di account."; "error_failed_creating_the_permalink" = "Impossibile creare il collegamento permanente"; "error_failed_loading_map" = "%1$@ non è riuscito a caricare la mappa. Riprova più tardi."; "error_failed_loading_messages" = "Caricamento dei messaggi non riuscito"; @@ -270,7 +272,7 @@ "error_some_messages_have_not_been_sent" = "Alcuni messaggi non sono stati inviati"; "error_unknown" = "Siamo spiacenti, si è verificato un errore"; "event_shield_reason_authenticity_not_guaranteed" = "L'autenticità di questo messaggio cifrato non può essere garantita su questo dispositivo."; -"event_shield_reason_previously_verified" = "Encrypted by a previously-verified user."; +"event_shield_reason_previously_verified" = "Cifrato da un utente precedentemente verificato."; "event_shield_reason_sent_in_clear" = "Non cifrato."; "event_shield_reason_unknown_device" = "Cifrato da un dispositivo sconosciuto o eliminato."; "event_shield_reason_unsigned_device" = "Cifrato da un dispositivo non verificato dal proprietario."; @@ -291,16 +293,15 @@ "notification_channel_silent" = "Notifiche silenziose"; "notification_incoming_call" = "Chiamata in arrivo"; "notification_inline_reply_failed" = "** Invio fallito - si prega di aprire la stanza"; -"notification_invitation_action_reject" = "Rifiuta"; "notification_invite_body" = "Ti ha invitato ad una conversazione"; -"notification_invite_body_with_sender" = "%1$@ invited you to chat"; +"notification_invite_body_with_sender" = "%1$@ ti ha invitato ad una conversazione"; "notification_mentioned_you_body" = "Ti ha menzionato: %1$@"; "notification_new_messages" = "Nuovi messaggi"; "notification_reaction_body" = "Ha reagito con %1$@"; "notification_room_invite_body" = "Ti ha invitato ad entrare nella stanza"; -"notification_room_invite_body_with_sender" = "%1$@ invited you to join the room"; +"notification_room_invite_body_with_sender" = "%1$@ ti ha invitato a unirti alla stanza"; "notification_sender_me" = "Io"; -"notification_sender_mention_reply" = "%1$@ mentioned or replied"; +"notification_sender_mention_reply" = "%1$@ menzionato o risposto"; "notification_test_push_notification_content" = "Stai visualizzando la notifica! Cliccami!"; "notification_ticker_text_dm" = "%1$@: %2$@"; "notification_ticker_text_group" = "%1$@: %2$@ %3$@"; @@ -335,26 +336,24 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "URL non valido, assicurati di includere il protocollo (http/https) e l'indirizzo corretto."; "screen_pinned_timeline_empty_state_description" = "Premi su un messaggio e scegli “%1$@” per includerlo qui."; "screen_pinned_timeline_empty_state_headline" = "Fissa i messaggi importanti così che possano essere trovati facilmente"; -"screen_pinned_timeline_screen_title_empty" = "Messaggi fissati"; -"screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; -"screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; -"screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; -"screen_resolve_send_failure_changed_identity_title" = "Your message was not sent because %1$@’s verified identity has changed"; -"screen_resolve_send_failure_unsigned_device_primary_button_title" = "Send message anyway"; -"screen_resolve_send_failure_unsigned_device_subtitle" = "%1$@ is using one or more unverified devices. You can send the message anyway, or you can cancel for now and try again later after %2$@ has verified all their devices."; -"screen_resolve_send_failure_unsigned_device_title" = "Your message was not sent because %1$@ has not verified all devices"; -"screen_resolve_send_failure_you_unsigned_device_subtitle" = "One or more of your devices are unverified. You can send the message anyway, or you can cancel for now and try again later after you have verified all of your devices."; -"screen_resolve_send_failure_you_unsigned_device_title" = "Your message was not sent because you have not verified one or more of your devices"; +"screen_reset_encryption_password_error" = "Si è verificato un errore sconosciuto. Controlla che la password del tuo account sia corretta e riprova."; +"screen_resolve_send_failure_changed_identity_primary_button_title" = "Ritira la verifica e invia"; +"screen_resolve_send_failure_changed_identity_subtitle" = "Puoi ritirare la tua verifica e inviare comunque questo messaggio, oppure annullarlo per ora e riprovare più tardi dopo aver riverificato %1$@."; +"screen_resolve_send_failure_changed_identity_title" = "Il tuo messaggio non è stato inviato perché l'identità verificata di %1$@ è cambiata."; +"screen_resolve_send_failure_unsigned_device_primary_button_title" = "Invia comunque il messaggio"; +"screen_resolve_send_failure_unsigned_device_subtitle" = "%1$@ sta usando uno o più dispositivi non verificati. Puoi inviare il messaggio in ogni caso, oppure annullarlo e riprovare più tardi quando %2$@ avrà verificato tutti i suoi dispositivi."; +"screen_resolve_send_failure_unsigned_device_title" = "Il tuo messaggio non è stato inviato perché %1$@ non ha verificato tutti i dispositivi."; +"screen_resolve_send_failure_you_unsigned_device_subtitle" = "Uno o più dispositivi non sono verificati. Puoi inviare il messaggio comunque, oppure annullarlo e riprovare più tardi dopo aver verificato tutti i tuoi dispositivi."; +"screen_resolve_send_failure_you_unsigned_device_title" = "Il tuo messaggio non è stato inviato perché non hai verificato uno o più dispositivi."; "screen_room_mentions_at_room_subtitle" = "Notifica l'intera stanza"; "screen_room_pinned_banner_indicator" = "%1$@ di %2$@"; "screen_room_pinned_banner_indicator_description" = "%1$@ Messaggi fissati"; "screen_room_pinned_banner_loading_description" = "Caricamento messaggio…"; "screen_room_pinned_banner_view_all_button_title" = "Mostra tutti"; "screen_room_details_pinned_events_row_title" = "Messaggi fissati"; -"screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; -"screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; -"screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "Cambia fornitore dell'account"; +"screen_timeline_item_menu_send_failure_changed_identity" = "Messaggio non inviato perché l'identità verificata di %1$@ è cambiata."; +"screen_timeline_item_menu_send_failure_unsigned_device" = "Messaggio non inviato perché %1$@ non ha verificato tutti i dispositivi."; +"screen_timeline_item_menu_send_failure_you_unsigned_device" = "Messaggio non inviato perché non hai verificato uno o più dispositivi."; "screen_account_provider_form_hint" = "Indirizzo dell'homeserver"; "screen_account_provider_form_notice" = "Inserisci un termine di ricerca o un indirizzo di dominio."; "screen_account_provider_form_subtitle" = "Cerca un'azienda, una comunità o un server privato."; @@ -432,11 +431,10 @@ "screen_chat_backup_key_backup_description" = "Il backup ti garantisce di non perdere la cronologia dei messaggi. %1$@."; "screen_chat_backup_key_backup_title" = "Backup"; "screen_chat_backup_recovery_action_change" = "Cambia la chiave di recupero"; -"screen_chat_backup_recovery_action_confirm" = "Conferma la chiave di recupero"; "screen_chat_backup_recovery_action_confirm_description" = "Il backup delle conversazioni non è attualmente sincronizzato."; "screen_chat_backup_recovery_action_setup" = "Configura il recupero"; "screen_chat_backup_recovery_action_setup_description" = "Ottieni l'accesso ai tuoi messaggi cifrati se perdi tutti i tuoi dispositivi o se sei disconnesso da %1$@ ovunque."; -"screen_create_account_title" = "Create account"; +"screen_create_account_title" = "Crea account"; "screen_create_new_recovery_key_list_item_1" = "Apri %1$@ in un dispositivo desktop"; "screen_create_new_recovery_key_list_item_2" = "Accedi nuovamente al tuo account"; "screen_create_new_recovery_key_list_item_3" = "Quando ti viene chiesto di verificare il tuo dispositivo, seleziona %1$@"; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Mostra i risultati solo al termine del sondaggio"; "screen_create_poll_anonymous_headline" = "Nascondi voti"; "screen_create_poll_answer_hint" = "Opzione %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Le tue modifiche non verranno salvate"; "screen_create_poll_cancel_confirmation_title_ios" = "Annulla sondaggio"; "screen_create_poll_question_desc" = "Domanda o argomento"; "screen_create_poll_question_hint" = "Di cosa parla il sondaggio?"; @@ -460,17 +457,17 @@ "screen_create_room_public_option_description" = "I messaggi non sono cifrati e chiunque può leggerli. Puoi attivare la crittografia in un secondo momento."; "screen_create_room_public_option_title" = "Stanza pubblica (chiunque)"; "screen_create_room_topic_label" = "Argomento (facoltativo)"; -"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"; -"screen_deactivate_account_delete_all_messages_notice" = "Warning: Future users may see incomplete conversations."; -"screen_deactivate_account_description" = "Deactivating your account is %1$@, it will:"; -"screen_deactivate_account_description_bold_part" = "irreversible"; -"screen_deactivate_account_list_item_1" = "%1$@ your account (you can't log back in, and your ID can't be reused)."; -"screen_deactivate_account_list_item_1_bold_part" = "Permanently disable"; -"screen_deactivate_account_list_item_2" = "Remove you from all chat rooms."; -"screen_deactivate_account_list_item_3" = "Delete your account information from our identity server."; -"screen_deactivate_account_list_item_4" = "Your messages will still be visible to registered users but won’t be available to new or unregistered users if you choose to delete them."; -"screen_deactivate_account_title" = "Deactivate account"; +"screen_deactivate_account_confirmation_dialog_content" = "Conferma di voler disattivare il tuo account. Questa azione è irreversibile."; +"screen_deactivate_account_delete_all_messages" = "Elimina tutti i miei messaggi"; +"screen_deactivate_account_delete_all_messages_notice" = "Attenzione: gli utenti futuri potrebbero vedere conversazioni incomplete."; +"screen_deactivate_account_description" = "La disattivazione del tuo account è %1$@ , quindi:"; +"screen_deactivate_account_description_bold_part" = "irreversibile"; +"screen_deactivate_account_list_item_1" = "%1$@ il tuo account (non puoi riaccedere e il tuo ID non può essere riutilizzato)."; +"screen_deactivate_account_list_item_1_bold_part" = "Disattiva permanentemente"; +"screen_deactivate_account_list_item_2" = "Ti rimuove da tutte le stanze di chat."; +"screen_deactivate_account_list_item_3" = "Elimina le informazioni del tuo account dal nostro server di identità."; +"screen_deactivate_account_list_item_4" = "I tuoi messaggi saranno ancora visibili agli utenti registrati, ma non saranno disponibili per gli utenti nuovi o non registrati se decidi di eliminarli."; +"screen_deactivate_account_title" = "Disattivazione dell'account"; "screen_edit_poll_delete_confirmation" = "Vuoi davvero eliminare questo sondaggio?"; "screen_edit_profile_display_name" = "Nome visualizzato"; "screen_edit_profile_display_name_placeholder" = "Il tuo nome visualizzato"; @@ -478,7 +475,7 @@ "screen_edit_profile_error_title" = "Impossibile aggiornare il profilo"; "screen_edit_profile_title" = "Modifica profilo"; "screen_edit_profile_updating_details" = "Aggiornamento del profilo…"; -"screen_encryption_reset_action_continue_reset" = "Continue reset"; +"screen_encryption_reset_action_continue_reset" = "Continua il ripristino"; "screen_encryption_reset_bullet_1" = "I dettagli del tuo account, i contatti, le preferenze e l'elenco delle conversazioni verranno conservati"; "screen_encryption_reset_bullet_2" = "Perderai la cronologia dei messaggi esistente"; "screen_encryption_reset_bullet_3" = "Dovrai verificare nuovamente tutti i dispositivi e i contatti esistenti"; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Chat di gruppo"; "screen_notification_settings_invite_for_me_label" = "Inviti"; "screen_notification_settings_mentions_only_disclaimer" = "Il tuo homeserver non supporta questa opzione nelle stanze crifrate, quindi potresti non ricevere notifiche in alcune stanze."; -"screen_notification_settings_mentions_section_title" = "Menzioni"; "screen_notification_settings_mode_all" = "Tutto"; "screen_notification_settings_mode_mentions" = "Menzioni"; "screen_notification_settings_notification_section_title" = "Avvisami per"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Inserisci..."; "screen_recovery_key_confirm_lost_recovery_key" = "Hai perso la chiave di recupero?"; "screen_recovery_key_confirm_success" = "Chiave di recupero confermata"; -"screen_recovery_key_confirm_title" = "Inserisci la tua chiave di recupero"; "screen_recovery_key_copied_to_clipboard" = "Chiave di recupero copiata"; "screen_recovery_key_generating_key" = "Generazione…"; "screen_recovery_key_save_action" = "Salva la chiave di recupero"; @@ -637,11 +632,10 @@ "screen_reset_encryption_confirmation_alert_action" = "Sì, reimposta ora"; "screen_reset_encryption_confirmation_alert_subtitle" = "Questo processo è irreversibile."; "screen_reset_encryption_confirmation_alert_title" = "Sei sicuro di voler reimpostare la crittografia?"; -"screen_reset_encryption_password_placeholder" = "Inserisci…"; "screen_reset_encryption_password_subtitle" = "Conferma di voler reimpostare la crittografia."; "screen_reset_encryption_password_title" = "Inserisci la password del tuo account per continuare"; -"screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; -"screen_reset_identity_confirmation_title" = "Can't confirm? Go to your account to reset your identity."; +"screen_reset_identity_confirmation_subtitle" = "Stai per accedere al tuo account di %1$@ per ripristinare la tua identità. Dopodiché verrai riportato all'app."; +"screen_reset_identity_confirmation_title" = "Non riesci a confermare? Vai al tuo account per ripristinare la tua identità."; "screen_room_alias_resolver_resolve_alias_failure" = "Impossibile risolvere l'alias della stanza."; "screen_room_attachment_source_camera" = "Fotocamera"; "screen_room_attachment_source_camera_video" = "Registra video"; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Gli amministratori hanno automaticamente i privilegi di moderatore"; "screen_room_change_role_moderators_title" = "Modifica moderatori"; "screen_room_change_role_unsaved_changes_description" = "Hai delle modifiche non salvate."; -"screen_room_change_role_unsaved_changes_title" = "Salvare le modifiche?"; "screen_room_details_add_topic_title" = "Aggiungi argomento"; "screen_room_details_already_a_member" = "Già membro"; "screen_room_details_already_invited" = "Già invitato"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Disattivazione del silenzioso di questa stanza fallita, riprova."; "screen_room_details_notification_mode_custom" = "Personalizzato"; "screen_room_details_notification_mode_default" = "Predefinito"; -"screen_room_details_notification_title" = "Notifiche"; "screen_room_details_share_room_title" = "Condividi stanza"; "screen_room_details_title" = "Informazioni sulla stanza"; "screen_room_details_updating_room" = "Aggiornamento della stanza…"; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Esclusione di %1$@"; "screen_room_member_list_manage_member_ban" = "Rimuovi ed escludi"; "screen_room_member_list_manage_member_remove" = "Rimuovi dalla stanza"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Rimuovi ed escludi"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Rimuovi soltanto"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Rimuovere e vietare l'accesso in futuro?"; "screen_room_member_list_manage_member_unban_action" = "Riammetti"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Segna come letto"; "screen_roomlist_mark_as_unread" = "Segna come non letto"; "screen_roomlist_room_directory_button_title" = "Sfoglia tutte le stanze"; -"screen_server_confirmation_change_server" = "Cambia fornitore dell'account"; "screen_server_confirmation_message_login_element_dot_io" = "Un server privato per i dipendenti di Element."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix è una rete aperta per comunicazioni sicure e decentralizzate."; "screen_server_confirmation_message_register" = "Qui è dove vivranno le tue conversazioni — proprio come useresti un fornitore di posta elettronica per conservare le tue email."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Stai per disconnettere la tua ultima sessione. Se esci ora, perderai l'accesso ai tuoi messaggi cifrati."; "screen_signout_key_backup_disabled_title" = "Hai disattivato il backup"; "screen_signout_key_backup_offline_subtitle" = "Il backup delle chiavi era ancora in corso quando sei andato offline. Riconnettiti per eseguire il backup delle chiavi prima di uscire."; -"screen_signout_key_backup_offline_title" = "Il backup delle chiavi è ancora in corso"; "screen_signout_key_backup_ongoing_subtitle" = "Attendi il completamento dell'operazione prima di uscire."; "screen_signout_key_backup_ongoing_title" = "Il backup delle chiavi è ancora in corso"; "screen_signout_recovery_disabled_subtitle" = "Stai per disconnettere la tua ultima sessione. Se esci ora, perderai l'accesso ai tuoi messaggi cifrati."; "screen_signout_recovery_disabled_title" = "Recupero non impostato"; "screen_signout_save_recovery_key_subtitle" = "Stai per disconnettere la tua ultima sessione. Se esci ora, potresti perdere l'accesso ai tuoi messaggi cifrati."; -"screen_signout_save_recovery_key_title" = "Hai salvato la chiave di recupero?"; "screen_start_chat_error_starting_chat" = "Si è verificato un errore durante il tentativo di avviare una chat"; "screen_view_location_title" = "Posizione"; "screen_welcome_bullet_1" = "Chiamate, sondaggi, ricerche e altro ancora saranno aggiunti nel corso dell'anno."; @@ -920,7 +908,6 @@ "test_language_identifier" = "it"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Risoluzione dei problemi"; -"troubleshoot_notifications_entry_point_title" = "Risoluzione di problemi delle notifiche"; "troubleshoot_notifications_screen_action" = "Esegui i test"; "troubleshoot_notifications_screen_action_again" = "Esegui nuovamente i test"; "troubleshoot_notifications_screen_failure" = "Alcuni test sono falliti. Si prega di controllare i dettagli."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Assicurati che i distributori UnifiedPush siano disponibili."; "troubleshoot_notifications_test_unified_push_failure" = "Nessun distributore di notifiche push trovato."; "troubleshoot_notifications_test_unified_push_title" = "Controlla UnifiedPush"; +"a11y_poll" = "Sondaggio"; "dialog_title_error" = "Errore"; "dialog_title_success" = "Operazione riuscita"; "notification_fallback_content" = "Notifica"; "notification_invitation_action_join" = "Entra"; +"notification_invitation_action_reject" = "Rifiuta"; "notification_room_action_mark_as_read" = "Segna come letto"; "notification_room_action_quick_reply" = "Risposta rapida"; +"screen_pinned_timeline_screen_title_empty" = "Messaggi fissati"; "screen_room_mentions_at_room_title" = "Tutti"; +"screen_account_provider_change" = "Cambia fornitore dell'account"; "screen_account_provider_signin_subtitle" = "Qui è dove vivranno le tue conversazioni — proprio come useresti un fornitore di posta elettronica per conservare le tue email."; "screen_account_provider_signup_subtitle" = "Qui è dove vivranno le tue conversazioni — proprio come useresti un fornitore di posta elettronica per conservare le tue email."; "screen_analytics_settings_help_us_improve" = "Condividi dati di utilizzo anonimi per aiutarci a identificare problemi."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Potrai vedere di nuovo tutti i suoi messaggi."; "screen_blocked_users_unblock_alert_title" = "Sblocca utente"; "screen_bug_report_rash_logs_alert_title" = "%1$@ si è chiuso inaspettatamente l'ultima volta che è stato usato. Vuoi condividere con noi un rapporto sull'arresto anomalo?"; +"screen_chat_backup_recovery_action_confirm" = "Inserisci la chiave di recupero"; +"screen_create_poll_cancel_confirmation_content_ios" = "Le modifiche non verranno salvate"; "screen_create_room_add_people_title" = "Invita persone"; "screen_create_room_room_name_label" = "Nome stanza"; "screen_create_room_title" = "Crea una stanza"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Modifica sondaggio"; "screen_identity_use_another_device" = "Usa un altro dispositivo"; "screen_login_subtitle" = "Matrix è una rete aperta per comunicazioni sicure e decentralizzate."; +"screen_notification_settings_mentions_section_title" = "Menzioni"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Riprova"; +"screen_recovery_key_confirm_title" = "Inserisci la chiave di recupero"; "screen_report_content_block_user" = "Blocca utente"; +"screen_reset_encryption_password_placeholder" = "Inserisci..."; "screen_room_attachment_source_camera_photo" = "Scatta foto"; "screen_room_change_permissions_everyone" = "Tutti"; "screen_room_change_permissions_member_moderation" = "Moderazione dei membri"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Amministratori"; "screen_room_change_role_section_moderators" = "Moderatori"; "screen_room_change_role_section_users" = "Membri"; +"screen_room_change_role_unsaved_changes_title" = "Salvare le modifiche?"; "screen_room_details_invite_people_title" = "Invita persone"; "screen_room_details_leave_conversation_title" = "Abbandona la conversazione"; "screen_room_details_leave_room_title" = "Esci dalla stanza"; +"screen_room_details_notification_title" = "Notifiche"; "screen_room_details_roles_and_permissions" = "Ruoli e autorizzazioni"; "screen_room_details_room_name_label" = "Nome stanza"; "screen_room_details_security_title" = "Sicurezza"; "screen_room_details_topic_title" = "Argomento"; "screen_room_error_failed_processing_media" = "Elaborazione del file multimediale da caricare fallita, riprova."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Rimuovi ed escludi"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Solo menzioni e parole chiave"; "screen_roomlist_filter_people" = "Persone"; +"screen_server_confirmation_change_server" = "Cambia fornitore dell'account"; "screen_signout_confirmation_dialog_submit" = "Disconnetti"; "screen_signout_confirmation_dialog_title" = "Disconnetti"; +"screen_signout_key_backup_offline_title" = "Il backup delle chiavi è ancora in corso"; "screen_signout_preference_item" = "Disconnetti"; +"screen_signout_save_recovery_key_title" = "Hai salvato la chiave di recupero?"; +"troubleshoot_notifications_entry_point_title" = "Risoluzione di problemi delle notifiche"; diff --git a/ElementX/Resources/Localizations/ka.lproj/Localizable.strings b/ElementX/Resources/Localizations/ka.lproj/Localizable.strings index 421186f9bb..342c48f297 100644 --- a/ElementX/Resources/Localizations/ka.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ka.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "პაუზა"; "a11y_pin_field" = "PIN ველი"; "a11y_play" = "დაკვრა"; -"a11y_poll" = "გამოკითხვა"; "a11y_poll_end" = "დასრულდა გამოკითხვა"; "a11y_react_with" = "რეაგირება %1$@-ით"; "a11y_react_with_other_emojis" = "რეაგირება სხვა ემოჯით"; @@ -41,6 +40,7 @@ "action_create" = "შექმნა"; "action_create_a_room" = "ოთახის შექმნა"; "action_deactivate" = "Deactivate"; +"action_deactivate_account" = "Deactivate account"; "action_decline" = "უარყოფა"; "action_delete_poll" = "გამოკითხვის წაშლა"; "action_disable" = "გამორთვა"; @@ -64,6 +64,7 @@ "action_leave" = "დატოვება"; "action_leave_conversation" = "Leave conversation"; "action_leave_room" = "ოთახის დატოვება"; +"action_load_more" = "მეტის ჩატვირთვა"; "action_manage_account" = "ანგარიშის მართვა"; "action_manage_devices" = "მოწყობილობების მართვა"; "action_message" = "Message"; @@ -93,6 +94,7 @@ "action_send_message" = "შეტყობინების გაგზავნა"; "action_share" = "გაზიარება"; "action_share_link" = "ბმულის გაზიარება"; +"action_show" = "Show"; "action_sign_in_again" = "ხელახლა შედით"; "action_signout" = "გამოსვლა"; "action_signout_anyway" = "მაინც გასვლა"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "View in timeline"; "action_view_source" = "წყაროს ნახვა"; "action_yes" = "დიახ"; -"action.load_more" = "მეტის ჩატვირთვა"; -"action_deactivate_account" = "Deactivate account"; "banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; @@ -162,6 +162,7 @@ "common_modern" = "თანამედროვე"; "common_mute" = "დადუმება"; "common_no_results" = "შედეგი არ არის"; +"common_no_room_name" = "No room name"; "common_offline" = "ხაზგარეშე"; "common_optic_id_ios" = "ოპტიკური ID"; "common_or" = "or"; @@ -170,6 +171,8 @@ "common_permalink" = "მუდმივი ბმული"; "common_permission" = "ნებართვა"; "common_please_wait" = "Please wait…"; +"common_poll_end_confirmation" = "დარწმუნებული ხართ, რომ გსურთ ამ გამოკითხვის დასრულება?"; +"common_poll_summary" = "გამოკითხვა: %1$@"; "common_poll_total_votes" = "სულ ხმები: %1$@"; "common_poll_undisclosed_text" = "შედეგები გამოკითხვის დასრულების შემდეგ გამოჩნდება"; "common_privacy_policy" = "კონფიდენციალურობის პოლიტიკა"; @@ -200,6 +203,7 @@ "common_settings" = "პარამეტრები"; "common_shared_location" = "გაზიარებული მდებარეობა"; "common_signing_out" = "გასვლა…"; +"common_something_went_wrong" = "Something went wrong"; "common_starting_chat" = "ჩატის დაწყება..."; "common_sticker" = "სტიკერი"; "common_success" = "წარმატება"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "რა თემებს ეხება ეს ოთახი?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "გაშიფვრა ვერ მოხერხდა"; +"common_unable_to_decrypt_no_access" = "You don't have access to this message"; "common_unable_to_invite_message" = "მოსაწვევები ვერ გაეგზავნა ერთ ან მეტ მომხმარებელს."; "common_unable_to_invite_title" = "მოწვევის (ების) გაგზავნა შეუძლებელია"; "common_unlock" = "განბლოკვა"; @@ -221,24 +226,21 @@ "common_username" = "მომხმარებლის სახელი"; "common_verification_cancelled" = "დადასტურება გაუქმდა"; "common_verification_complete" = "დადასტურება დასრულებულია"; +"common_verify_device" = "დაადასტურეთ მოწყობილობა"; "common_video" = "ვიდეო"; "common_voice_message" = "ხმოვანი შეტყობინება"; "common_waiting" = "მოცდა..."; "common_waiting_for_decryption_key" = "ლოდინი ამ შეტყობინებისათვის"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "Do not show this again"; "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "Send to"; "common.you" = "You"; -"common_no_room_name" = "No room name"; -"common_poll_end_confirmation" = "დარწმუნებული ხართ, რომ გსურთ ამ გამოკითხვის დასრულება?"; -"common_poll_summary" = "გამოკითხვა: %1$@"; -"common_something_went_wrong" = "Something went wrong"; -"common_unable_to_decrypt_no_access" = "You don't have access to this message"; -"common_verify_device" = "დაადასტურეთ მოწყობილობა"; "confirm_recovery_key_banner_message" = "თქვენი ჩეთების სარეზერვო ასლი ამჟამად არ არის სინქრონიზებული. თქვენ უნდა შეიყვანოთ თქვენი აღდგენის გასაღები, რათა შეინარჩუნოთ წვდომა ჩეთების სარეზერვო ასლზე."; "confirm_recovery_key_banner_title" = "შეიყვანეთ აღდგენის გასაღები"; "crash_detection_dialog_content" = "%1$@ ავარიულად გაითიშა ბოლოს გამოიყენებისას. გსურთ, გამოგვიგზავნოთ ავარიული გათიშვის ჟურნალი?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "იმისათვის, რომ აპლიკაციამ გამოიყენოს კამერა, გთხოვთ, მიანიჭოთ ნებართვა სისტემის პარამეტრებში."; "dialog_permission_generic" = "გთხოვთ, მიანიჭოთ ნებართვა სისტემის პარამეტრებში."; "dialog_permission_location_description_ios" = "მიანიჭეთ წვდომა პარამეტრებში -> ლოკაციაში."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "ჩუმი შეტყობინებები"; "notification_incoming_call" = "Incoming call"; "notification_inline_reply_failed" = "** გაგზავნა ვერ მოხერხდა - გთხოვთ, გახსნათ ოთახი"; -"notification_invitation_action_reject" = "უარყოფა"; "notification_invite_body" = "მოგიწვიათ ჩატში"; "notification_invite_body_with_sender" = "%1$@ invited you to chat"; "notification_mentioned_you_body" = "მოგახსენათ: %1$@"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "არასწორი URL, გთხოვთ, დარწმუნდეთ, რომ შეიტანეთ პროტოკოლი (http/https) და სწორი მისამართი."; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; -"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; "screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "ანგარიშის მიმწოდებლის შეცვლა"; "screen_account_provider_form_hint" = "სახლის სერვერის მისამართი"; "screen_account_provider_form_notice" = "შეიყვანეთ საძიებო სიტყვა ან დომენის მისამართი."; "screen_account_provider_form_subtitle" = "მოძებნეთ კომპანია, საზოგადოება ან კერძო სერვერი."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "სარეზერვო ასლი უზრუნველყოფს იმას, რომ თქვენ შეტყობინებების ისტორიას არ დაკარგავთ. %1$@"; "screen_chat_backup_key_backup_title" = "სარეზერვო ასლი"; "screen_chat_backup_recovery_action_change" = "აღდგენის გასაღების შეცვლა"; -"screen_chat_backup_recovery_action_confirm" = "შეიყვანეთ აღდგენის გასაღები"; "screen_chat_backup_recovery_action_confirm_description" = "თქვენი ჩატის სარეზერვო ასლი ამჟამად არ არის სინქრონიზებული."; "screen_chat_backup_recovery_action_setup" = "აღდგენის დაყენება"; "screen_chat_backup_recovery_action_setup_description" = "მიიღეთ წვდომა თქვენს დაშიფრულ შეტყობინებებზე, თუ დაკარგავთ თქვენს ყველა მოწყობილობას ან გამოხვალთ სისტემიდან %1$@-დან ყველგან."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "შედეგების ჩვენება მხოლოდ გამოკითხვის დასრულების შემდეგ"; "screen_create_poll_anonymous_headline" = "ხმების დამალვა"; "screen_create_poll_answer_hint" = "ვარიანტი %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "თქვენი ცვლილებები არ შეინახება"; "screen_create_poll_cancel_confirmation_title_ios" = "გამოკითხვის გაუქმება"; "screen_create_poll_question_desc" = "კითხვა ან თემა"; "screen_create_poll_question_hint" = "რას ეხება გამოკითხვა?"; @@ -480,7 +477,7 @@ "screen_edit_profile_updating_details" = "პროფილის განახლება..."; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history"; +"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "ჯგუფური ჩატები"; "screen_notification_settings_invite_for_me_label" = "მოსაწვევები"; "screen_notification_settings_mentions_only_disclaimer" = "თქვენი სახლის სერვერი არ უჭერს მხარს ამ პარამეტრს დაშიფრულ ოთახებში, ზოგიერთ ოთახში შეიძლება არ მიიღოთ შეტყობინება."; -"screen_notification_settings_mentions_section_title" = "ხსენებები"; "screen_notification_settings_mode_all" = "ყველა"; "screen_notification_settings_mode_mentions" = "ხსენებები"; "screen_notification_settings_notification_section_title" = "ჩემი შეტყობინება შემდეგისთვის:"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "შეყვანა"; "screen_recovery_key_confirm_lost_recovery_key" = "Lost your recovery key?"; "screen_recovery_key_confirm_success" = "აღდგენის გასაღები დადასტურებულია"; -"screen_recovery_key_confirm_title" = "შეიყვანეთ თქვენი აღდგენის გასაღები"; "screen_recovery_key_copied_to_clipboard" = "დაკოპირებულია აღდგენის გასაღები"; "screen_recovery_key_generating_key" = "გენერირება..."; "screen_recovery_key_save_action" = "აღდგენის გასაღების შენახვა"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Yes, reset now"; "screen_reset_encryption_confirmation_alert_subtitle" = "This process is irreversible."; "screen_reset_encryption_confirmation_alert_title" = "Are you sure you want to reset your identity?"; -"screen_reset_encryption_password_placeholder" = "Enter…"; "screen_reset_encryption_password_subtitle" = "Confirm that you want to reset your identity."; "screen_reset_encryption_password_title" = "Enter your account password to continue"; "screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Admins automatically have moderator privileges"; "screen_room_change_role_moderators_title" = "Edit Moderators"; "screen_room_change_role_unsaved_changes_description" = "You have unsaved changes."; -"screen_room_change_role_unsaved_changes_title" = "Save changes?"; "screen_room_details_add_topic_title" = "თემის დამატება"; "screen_room_details_already_a_member" = "უკვე წევრია"; "screen_room_details_already_invited" = "უკვე მოწვეულია"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "ამ ოთახის დადუმების მოხსნა ვერ მოხერხდა. გთხოვთ, სცადოთ ხელახლა."; "screen_room_details_notification_mode_custom" = "მორგებული"; "screen_room_details_notification_mode_default" = "ნაგულისხმევი"; -"screen_room_details_notification_title" = "შეტყობინებები"; "screen_room_details_share_room_title" = "ოთახის გაზიარება"; "screen_room_details_title" = "Room info"; "screen_room_details_updating_room" = "ოთახის განახლება..."; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Banning %1$@"; "screen_room_member_list_manage_member_ban" = "Remove and ban member"; "screen_room_member_list_manage_member_remove" = "Remove from room"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Remove and ban member"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Only remove member"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Remove member and ban from joining in the future?"; "screen_room_member_list_manage_member_unban_action" = "Unban"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Mark as read"; "screen_roomlist_mark_as_unread" = "Mark as unread"; "screen_roomlist_room_directory_button_title" = "Browse all rooms"; -"screen_server_confirmation_change_server" = "შეცვალეთ ანგარიშის მომწოდებელი"; "screen_server_confirmation_message_login_element_dot_io" = "კერძო სერვერი Element-ის თანამშრომლებისთვის."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix არის ღია ქსელი უსაფრთხო, დეცენტრალიზებული კომუნიკაციისთვის."; "screen_server_confirmation_message_register" = "აქ იქნება თქვენი საუბრები - ისევე, როგორც თქვენ ელ. ფოსტაში ინახება თქვენი ელ.წერილები."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "თქვენ აპირებთ გასვლას თქვენი ბოლო სესიიდან. თუ ახლა გამოხვალთ, დაკარგავთ წვდომას თქვენს დაშიფრულ შეტყობინებებზე."; "screen_signout_key_backup_disabled_title" = "თქვენ გამორთეთ სარეზერვო ასლი"; "screen_signout_key_backup_offline_subtitle" = "თქვენი გასაღებების სარეზერვო ასლის შექმნა მიმდინარეობდა იმ დროს, როდესაც გამოხვედით. დაკავშირდით ისევ ისე, რომ სარეზერვო ასლი შეიქმნას ანგარიშიდან გამოსვლის გარეშე."; -"screen_signout_key_backup_offline_title" = "თქვენი გასაღებების სარეზერვო ასლი ჯერ კიდევ შექმნის პროცესშია"; "screen_signout_key_backup_ongoing_subtitle" = "გთხოვთ დაელოდეთ ამის დასრულებას სისტემიდან გამოსვლამდე."; "screen_signout_key_backup_ongoing_title" = "თქვენი გასაღებების სარეზერვო ასლი ჯერ კიდევ შექმნის პროცესშია"; "screen_signout_recovery_disabled_subtitle" = "თქვენ აპირებთ გასვლას თქვენი ბოლო სესიიდან. თუ ახლა გამოხვალთ, დაკარგავთ წვდომას თქვენს დაშიფრულ შეტყობინებებზე."; "screen_signout_recovery_disabled_title" = "აღდგენა არ არის დაყენებული"; "screen_signout_save_recovery_key_subtitle" = "თქვენ აპირებთ გასვლას თქვენი ბოლო სესიიდან. თუ ახლა გამოხვალთ, შესაძლოა დაკარგოთ წვდომა თქვენს დაშიფრულ შეტყობინებებზე."; -"screen_signout_save_recovery_key_title" = "შეინახეთ თქვენი აღდგენის გასაღები?"; "screen_start_chat_error_starting_chat" = "ჩატის დაწყების მცდელობისას შეცდომა მოხდა"; "screen_view_location_title" = "ადგილმდებარეობა"; "screen_welcome_bullet_1" = "ზარები, გამოკითხვები, ძიება და სხვა დაემატება ამ წლის ბოლოს."; @@ -920,7 +908,6 @@ "test_language_identifier" = "ka"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Troubleshoot"; -"troubleshoot_notifications_entry_point_title" = "Troubleshoot notifications"; "troubleshoot_notifications_screen_action" = "Run tests"; "troubleshoot_notifications_screen_action_again" = "Run tests again"; "troubleshoot_notifications_screen_failure" = "Some tests failed. Please check the details."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Ensure that UnifiedPush distributors are available."; "troubleshoot_notifications_test_unified_push_failure" = "No push distributors found."; "troubleshoot_notifications_test_unified_push_title" = "Check UnifiedPush"; +"a11y_poll" = "გამოკითხვა"; "dialog_title_error" = "შეცდომა"; "dialog_title_success" = "წარმატება"; "notification_fallback_content" = "შეტყობინება"; "notification_invitation_action_join" = "გაწევრიანება"; +"notification_invitation_action_reject" = "Reject"; "notification_room_action_mark_as_read" = "Mark as read"; "notification_room_action_quick_reply" = "Სწრაფი პასუხი"; +"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_room_mentions_at_room_title" = "ყველა"; +"screen_account_provider_change" = "შეცვალეთ ანგარიშის მომწოდებელი"; "screen_account_provider_signin_subtitle" = "აქ იქნება თქვენი საუბრები - ისევე, როგორც თქვენ ელ. ფოსტაში ინახება თქვენი ელ.წერილები."; "screen_account_provider_signup_subtitle" = "აქ იქნება თქვენი საუბრები - ისევე, როგორც თქვენ ელ. ფოსტაში ინახება თქვენი ელ.წერილები."; "screen_analytics_settings_help_us_improve" = "გააზიარეთ ანონიმური გამოყენების მონაცემები, რათა დაგვეხმაროთ პრობლემების იდენტიფიცირებაში."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "თქვენ კვლავ შეძლებთ მათგან ყველა შეტყობინების ნახვას."; "screen_blocked_users_unblock_alert_title" = "Მომხმარებლის განბლოკვა"; "screen_bug_report_rash_logs_alert_title" = "%1$@ ავარიულად გაითიშა ბოლოს გამოიყენებისას. გსურთ, გამოგვიგზავნოთ ავარიული გათიშვის ჟურნალი?"; +"screen_chat_backup_recovery_action_confirm" = "Enter recovery key"; +"screen_create_poll_cancel_confirmation_content_ios" = "Your changes won’t be saved"; "screen_create_room_add_people_title" = "ხალხის მოწვევა"; "screen_create_room_room_name_label" = "ოთახის სახელი"; "screen_create_room_title" = "ოთახის შექმნა"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "გამოკითხვის რედაქტირება"; "screen_identity_use_another_device" = "Use another device"; "screen_login_subtitle" = "Matrix არის ღია ქსელი უსაფრთხო, დეცენტრალიზებული კომუნიკაციისთვის."; +"screen_notification_settings_mentions_section_title" = "ხსენებები"; "screen_qr_code_login_invalid_scan_state_retry_button" = "ხელახლა ცდა"; +"screen_recovery_key_confirm_title" = "შეიყვანეთ აღდგენის გასაღები"; "screen_report_content_block_user" = "მომხმარებლის დაბლოკვა"; +"screen_reset_encryption_password_placeholder" = "შეყვანა"; "screen_room_attachment_source_camera_photo" = "ფოტოს გადაღება"; "screen_room_change_permissions_everyone" = "ყველა"; "screen_room_change_permissions_member_moderation" = "Member moderation"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Admins"; "screen_room_change_role_section_moderators" = "Moderators"; "screen_room_change_role_section_users" = "Members"; +"screen_room_change_role_unsaved_changes_title" = "Save changes?"; "screen_room_details_invite_people_title" = "ხალხის მოწვევა"; "screen_room_details_leave_conversation_title" = "Leave conversation"; "screen_room_details_leave_room_title" = "ოთახის დატოვება"; +"screen_room_details_notification_title" = "შეტყობინებები"; "screen_room_details_roles_and_permissions" = "Roles and permissions"; "screen_room_details_room_name_label" = "ოთახის სახელი"; "screen_room_details_security_title" = "უსაფრთხოება"; "screen_room_details_topic_title" = "თემა"; "screen_room_error_failed_processing_media" = "მედიის ატვირთვა ვერ მოხერხდა. გთხოვთ, სცადოთ ხელახლა."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Remove and ban member"; "screen_room_notification_settings_mode_mentions_and_keywords" = "მხოლოდ ხსენებები და საკვანძო სიტყვები"; "screen_roomlist_filter_people" = "ხალხი"; +"screen_server_confirmation_change_server" = "შეცვალეთ ანგარიშის მომწოდებელი"; "screen_signout_confirmation_dialog_submit" = "გამოსვლა"; "screen_signout_confirmation_dialog_title" = "გამოსვლა"; +"screen_signout_key_backup_offline_title" = "თქვენი გასაღებების სარეზერვო ასლი ჯერ კიდევ შექმნის პროცესშია"; "screen_signout_preference_item" = "გამოსვლა"; +"screen_signout_save_recovery_key_title" = "შეინახეთ თქვენი აღდგენის გასაღები?"; +"troubleshoot_notifications_entry_point_title" = "Troubleshoot notifications"; diff --git a/ElementX/Resources/Localizations/nl.lproj/Localizable.strings b/ElementX/Resources/Localizations/nl.lproj/Localizable.strings index eb86c71edd..1b308038b7 100644 --- a/ElementX/Resources/Localizations/nl.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/nl.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Pauzeren"; "a11y_pin_field" = "PIN-veld"; "a11y_play" = "Afspelen"; -"a11y_poll" = "Peiling"; "a11y_poll_end" = "Beeïndigde peiling"; "a11y_react_with" = "Reageer met %1$@"; "a11y_react_with_other_emojis" = "Reageer met andere emoji's"; @@ -41,6 +40,7 @@ "action_create" = "Aanmaken"; "action_create_a_room" = "Creëer een kamer"; "action_deactivate" = "Deactivate"; +"action_deactivate_account" = "Deactivate account"; "action_decline" = "Weigeren"; "action_delete_poll" = "Peiling verwijderen"; "action_disable" = "Uitschakelen"; @@ -64,6 +64,7 @@ "action_leave" = "Verlaten"; "action_leave_conversation" = "Gesprek verlaten"; "action_leave_room" = "Ruimte verlaten"; +"action_load_more" = "Meer laden"; "action_manage_account" = "Account beheren"; "action_manage_devices" = "Apparaten beheren"; "action_message" = "Bericht"; @@ -93,6 +94,7 @@ "action_send_message" = "Bericht verzenden"; "action_share" = "Delen"; "action_share_link" = "Link delen"; +"action_show" = "Show"; "action_sign_in_again" = "Log opnieuw in"; "action_signout" = "Uitloggen"; "action_signout_anyway" = "Toch uitloggen"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "View in timeline"; "action_view_source" = "Bron weergeven"; "action_yes" = "Ja"; -"action.load_more" = "Meer laden"; -"action_deactivate_account" = "Deactivate account"; "banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; @@ -162,6 +162,7 @@ "common_modern" = "Modern"; "common_mute" = "Dempen"; "common_no_results" = "Geen resultaten"; +"common_no_room_name" = "Geen kamernaam"; "common_offline" = "Offline"; "common_optic_id_ios" = "Optic ID"; "common_or" = "of"; @@ -170,6 +171,8 @@ "common_permalink" = "Permalink"; "common_permission" = "Toestemming"; "common_please_wait" = "Even geduld..."; +"common_poll_end_confirmation" = "Weet je zeker dat je deze peiling wilt beëindigen?"; +"common_poll_summary" = "Peiling: %1$@"; "common_poll_total_votes" = "Totaal aantal stemmen: %1$@"; "common_poll_undisclosed_text" = "Resultaten worden getoond nadat de peiling is afgelopen"; "common_privacy_policy" = "Privacybeleid"; @@ -200,6 +203,7 @@ "common_settings" = "Instellingen"; "common_shared_location" = "Gedeelde locatie"; "common_signing_out" = "Uitloggen"; +"common_something_went_wrong" = "Er is iets misgegaan"; "common_starting_chat" = "Chat starten…"; "common_sticker" = "Sticker"; "common_success" = "Geslaagd"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "Waar gaat deze kamer over?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Kan niet ontsleutelen"; +"common_unable_to_decrypt_no_access" = "Je hebt geen toegang tot dit bericht"; "common_unable_to_invite_message" = "Uitnodigingen konden niet naar een of meerdere gebruikers worden verzonden."; "common_unable_to_invite_title" = "Kan uitnodiging(en) niet verzenden"; "common_unlock" = "Ontgrendelen"; @@ -221,24 +226,21 @@ "common_username" = "Gebruikersnaam"; "common_verification_cancelled" = "Verificatie geannuleerd"; "common_verification_complete" = "Verificatie voltooid"; +"common_verify_device" = "Apparaat verifiëren"; "common_video" = "Video"; "common_voice_message" = "Spraakbericht"; "common_waiting" = "Wachten…"; "common_waiting_for_decryption_key" = "Wachten op dit bericht"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "Do not show this again"; "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "Send to"; "common.you" = "You"; -"common_no_room_name" = "Geen kamernaam"; -"common_poll_end_confirmation" = "Weet je zeker dat je deze peiling wilt beëindigen?"; -"common_poll_summary" = "Peiling: %1$@"; -"common_something_went_wrong" = "Er is iets misgegaan"; -"common_unable_to_decrypt_no_access" = "Je hebt geen toegang tot dit bericht"; -"common_verify_device" = "Apparaat verifiëren"; "confirm_recovery_key_banner_message" = "Je chatback-up is momenteel niet gesynchroniseerd. Je moet je herstelsleutel invoeren om toegang te behouden tot je chatback-up."; "confirm_recovery_key_banner_title" = "Voer je herstelsleutel in"; "crash_detection_dialog_content" = "%1$@ crashte de laatste keer dat het werd gebruikt. Wil je een crashrapport met ons delen?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "Geef toestemming in de systeeminstellingen om de applicatie de camera te laten gebruiken."; "dialog_permission_generic" = "Geef hiervoor toestemming in de systeeminstellingen."; "dialog_permission_location_description_ios" = "Verleen toegang via Instellingen -> Locatie."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "Stille meldingen"; "notification_incoming_call" = "Incoming call"; "notification_inline_reply_failed" = "** Verzenden is mislukt - open de kamer"; -"notification_invitation_action_reject" = "Afwijzen"; "notification_invite_body" = "Nodigde je uit om te chatten"; "notification_invite_body_with_sender" = "%1$@ invited you to chat"; "notification_mentioned_you_body" = "Heeft je genoemd: %1$@"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "Ongeldige URL, zorg ervoor dat je het protocol (http/https) en het juiste adres invult."; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; -"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; "screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "Wijzig accountprovider"; "screen_account_provider_form_hint" = "Homeserver-adres"; "screen_account_provider_form_notice" = "Voer een zoekterm of een domeinnaam in."; "screen_account_provider_form_subtitle" = "Zoek naar een bedrijf, community of privéserver."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "Een back-up maken zorgt ervoor dat je je berichtgeschiedenis niet verliest. %1$@."; "screen_chat_backup_key_backup_title" = "Back-up"; "screen_chat_backup_recovery_action_change" = "Herstelsleutel wijzigen"; -"screen_chat_backup_recovery_action_confirm" = "Voer herstelsleutel in"; "screen_chat_backup_recovery_action_confirm_description" = "Je chatback-up is momenteel niet gesynchroniseerd."; "screen_chat_backup_recovery_action_setup" = "Herstelmogelijkheid instellen"; "screen_chat_backup_recovery_action_setup_description" = "Krijg toegang tot je versleutelde berichten als je al je apparaten kwijtraakt of overal uit %1$@ bent uitgelogd."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Resultaten pas weergeven nadat de peiling is afgelopen"; "screen_create_poll_anonymous_headline" = "Stemmen verbergen"; "screen_create_poll_answer_hint" = "Optie %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Je wijzigingen worden niet opgeslagen"; "screen_create_poll_cancel_confirmation_title_ios" = "Peiling annuleren"; "screen_create_poll_question_desc" = "Vraag of onderwerp"; "screen_create_poll_question_hint" = "Waar gaat de peiling over?"; @@ -480,7 +477,7 @@ "screen_edit_profile_updating_details" = "Profiel bijwerken…"; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history"; +"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Groep chats"; "screen_notification_settings_invite_for_me_label" = "Uitnodigingen"; "screen_notification_settings_mentions_only_disclaimer" = "Je homeserver ondersteunt deze optie niet in versleutelde kamers; in sommige kamers krijg je mogelijk geen meldingen."; -"screen_notification_settings_mentions_section_title" = "Vermeldingen"; "screen_notification_settings_mode_all" = "Alles"; "screen_notification_settings_mode_mentions" = "Vermeldingen"; "screen_notification_settings_notification_section_title" = "Stuur me een melding voor"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Voer in..."; "screen_recovery_key_confirm_lost_recovery_key" = "Herstelsleutel kwijt?"; "screen_recovery_key_confirm_success" = "Herstelsleutel bevestigd"; -"screen_recovery_key_confirm_title" = "Voer je herstelsleutel in"; "screen_recovery_key_copied_to_clipboard" = "Herstelsleutel gekopieerd"; "screen_recovery_key_generating_key" = "Genereren..."; "screen_recovery_key_save_action" = "Herstelsleutel opslaan"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Yes, reset now"; "screen_reset_encryption_confirmation_alert_subtitle" = "This process is irreversible."; "screen_reset_encryption_confirmation_alert_title" = "Are you sure you want to reset your identity?"; -"screen_reset_encryption_password_placeholder" = "Enter…"; "screen_reset_encryption_password_subtitle" = "Confirm that you want to reset your identity."; "screen_reset_encryption_password_title" = "Enter your account password to continue"; "screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Beheerders hebben automatisch moderatorrechten"; "screen_room_change_role_moderators_title" = "Moderators bewerken"; "screen_room_change_role_unsaved_changes_description" = "Je hebt niet-opgeslagen wijzigingen"; -"screen_room_change_role_unsaved_changes_title" = "Wijzigingen opslaan?"; "screen_room_details_add_topic_title" = "Onderwerp toevoegen"; "screen_room_details_already_a_member" = "Reeds lid"; "screen_room_details_already_invited" = "Reeds uitgenodigd"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Het dempen opheffen voor deze kamer is mislukt. Probeer het opnieuw."; "screen_room_details_notification_mode_custom" = "Aangepast"; "screen_room_details_notification_mode_default" = "Standaard"; -"screen_room_details_notification_title" = "Meldingen"; "screen_room_details_share_room_title" = "Kamer delen"; "screen_room_details_title" = "Kamer info"; "screen_room_details_updating_room" = "Kamer bijwerken…"; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "%1$@ verbannen"; "screen_room_member_list_manage_member_ban" = "Lid verwijderen en verbannen"; "screen_room_member_list_manage_member_remove" = "Verwijderen uit kamer"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Lid verwijderen en verbannen"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Alleen lid verwijderen"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Lid verwijderen en toekomstige deelname verbieden?"; "screen_room_member_list_manage_member_unban_action" = "Ontbannen"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Markeren als gelezen"; "screen_roomlist_mark_as_unread" = "Markeren als ongelezen"; "screen_roomlist_room_directory_button_title" = "Blader door alle kamers"; -"screen_server_confirmation_change_server" = "Accountprovider wijzigen"; "screen_server_confirmation_message_login_element_dot_io" = "Een privéserver voor medewerkers van Element."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix is een open netwerk voor veilige, gedecentraliseerde communicatie."; "screen_server_confirmation_message_register" = "Dit is waar je gesprekken zullen worden bewaard — net zoals je een e-mailprovider zou gebruiken om je e-mails te bewaren."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Je staat op het punt uit te loggen bij je laatste sessie. Als je je nu uitlogt, verlies je de toegang tot je versleutelde berichten."; "screen_signout_key_backup_disabled_title" = "Je hebt de back-up uitgeschakeld"; "screen_signout_key_backup_offline_subtitle" = "De backup van je sleutels was nog bezig toen je offline ging. Maak opnieuw verbinding zodat er een back-up van je sleutels kan worden gemaakt voordat je uitlogt."; -"screen_signout_key_backup_offline_title" = "De backup van je sleutels is nog bezig"; "screen_signout_key_backup_ongoing_subtitle" = "Wacht tot dit voltooid is voordat je uitlogt."; "screen_signout_key_backup_ongoing_title" = "De backup van je sleutels is nog bezig"; "screen_signout_recovery_disabled_subtitle" = "Je staat op het punt uit te loggen bij je laatste sessie. Als je je nu uitlogt, verlies je de toegang tot je versleutelde berichten."; "screen_signout_recovery_disabled_title" = "Herstelmogelijkheid niet ingesteld"; "screen_signout_save_recovery_key_subtitle" = "Je staat op het punt uit te loggen bij je laatste sessie. Als je je nu uitlogt, kan het dat je de toegang tot je versleutelde berichten verliest."; -"screen_signout_save_recovery_key_title" = "Heb je je herstelsleutel opgeslagen?"; "screen_start_chat_error_starting_chat" = "Er is een fout opgetreden bij het starten van een chat"; "screen_view_location_title" = "Locatie"; "screen_welcome_bullet_1" = "Oproepen, peilingen, zoekopdrachten en meer zullen later dit jaar worden toegevoegd."; @@ -920,7 +908,6 @@ "test_language_identifier" = "en"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Problemen oplossen"; -"troubleshoot_notifications_entry_point_title" = "Problemen met meldingen oplossen"; "troubleshoot_notifications_screen_action" = "Tests uitvoeren"; "troubleshoot_notifications_screen_action_again" = "Tests opnieuw uitvoeren"; "troubleshoot_notifications_screen_failure" = "Sommige tests zijn mislukt. Controleer de details."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Ervoor zorgen dat UnifiedPush verdelers beschikbaar zijn."; "troubleshoot_notifications_test_unified_push_failure" = "Geen push-verdelers gevonden."; "troubleshoot_notifications_test_unified_push_title" = "UnifiedPush controleren"; +"a11y_poll" = "Peiling"; "dialog_title_error" = "Fout"; "dialog_title_success" = "Geslaagd"; "notification_fallback_content" = "Melding"; "notification_invitation_action_join" = "Deelnemen"; +"notification_invitation_action_reject" = "Reject"; "notification_room_action_mark_as_read" = "Markeren als gelezen"; "notification_room_action_quick_reply" = "Snel antwoord"; +"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_room_mentions_at_room_title" = "Iedereen"; +"screen_account_provider_change" = "Wijzig accountprovider"; "screen_account_provider_signin_subtitle" = "Dit is waar je gesprekken zullen worden bewaard — net zoals je een e-mailprovider zou gebruiken om je e-mails te bewaren."; "screen_account_provider_signup_subtitle" = "Dit is waar je gesprekken zullen worden bewaard — net zoals je een e-mailprovider zou gebruiken om je e-mails te bewaren."; "screen_analytics_settings_help_us_improve" = "Deel anonieme gebruiksgegevens om ons te helpen problemen te identificeren."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Je zult alle berichten van hen weer kunnen zien."; "screen_blocked_users_unblock_alert_title" = "Gebruiker deblokkeren"; "screen_bug_report_rash_logs_alert_title" = "%1$@ crashte de laatste keer dat het werd gebruikt. Wil je een crashrapport met ons delen?"; +"screen_chat_backup_recovery_action_confirm" = "Voer herstelsleutel in"; +"screen_create_poll_cancel_confirmation_content_ios" = "Je wijzigingen worden niet opgeslagen"; "screen_create_room_add_people_title" = "Mensen uitnodigen"; "screen_create_room_room_name_label" = "Naam van de kamer"; "screen_create_room_title" = "Creëer een kamer"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Peiling wijzigen"; "screen_identity_use_another_device" = "Use another device"; "screen_login_subtitle" = "Matrix is een open netwerk voor veilige, gedecentraliseerde communicatie."; +"screen_notification_settings_mentions_section_title" = "Vermeldingen"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Probeer het opnieuw"; +"screen_recovery_key_confirm_title" = "Voer je herstelsleutel in"; "screen_report_content_block_user" = "Gebruiker blokkeren"; +"screen_reset_encryption_password_placeholder" = "Voer in..."; "screen_room_attachment_source_camera_photo" = "Foto maken"; "screen_room_change_permissions_everyone" = "Iedereen"; "screen_room_change_permissions_member_moderation" = "Moderatie van leden"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Beheerders"; "screen_room_change_role_section_moderators" = "Moderators"; "screen_room_change_role_section_users" = "Leden"; +"screen_room_change_role_unsaved_changes_title" = "Wijzigingen opslaan?"; "screen_room_details_invite_people_title" = "Mensen uitnodigen"; "screen_room_details_leave_conversation_title" = "Gesprek verlaten"; "screen_room_details_leave_room_title" = "Ruimte verlaten"; +"screen_room_details_notification_title" = "Meldingen"; "screen_room_details_roles_and_permissions" = "Rollen en rechten"; "screen_room_details_room_name_label" = "Naam van de kamer"; "screen_room_details_security_title" = "Beveiliging"; "screen_room_details_topic_title" = "Onderwerp"; "screen_room_error_failed_processing_media" = "Het verwerken van media voor uploaden is mislukt. Probeer het opnieuw."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Lid verwijderen en verbannen"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Alleen vermeldingen en trefwoorden"; "screen_roomlist_filter_people" = "Personen"; +"screen_server_confirmation_change_server" = "Wijzig accountprovider"; "screen_signout_confirmation_dialog_submit" = "Uitloggen"; "screen_signout_confirmation_dialog_title" = "Uitloggen"; +"screen_signout_key_backup_offline_title" = "De backup van je sleutels is nog bezig"; "screen_signout_preference_item" = "Uitloggen"; +"screen_signout_save_recovery_key_title" = "Heb je je herstelsleutel opgeslagen?"; +"troubleshoot_notifications_entry_point_title" = "Problemen met meldingen oplossen"; diff --git a/ElementX/Resources/Localizations/pl.lproj/Localizable.strings b/ElementX/Resources/Localizations/pl.lproj/Localizable.strings index 17f52505f6..b7fdaff6f5 100644 --- a/ElementX/Resources/Localizations/pl.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pl.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Wstrzymaj"; "a11y_pin_field" = "Pole PIN"; "a11y_play" = "Odtwórz"; -"a11y_poll" = "Ankieta"; "a11y_poll_end" = "Zakończona ankieta"; "a11y_react_with" = "Zareaguj z %1$@"; "a11y_react_with_other_emojis" = "Zareaguj innym emoji"; @@ -27,20 +26,21 @@ "action_back" = "Wróć"; "action_call" = "Zadzwoń"; "action_cancel" = "Anuluj"; -"action_cancel_for_now" = "Cancel for now"; +"action_cancel_for_now" = "Anuluj na razie"; "action_choose_photo" = "Wybierz zdjęcie"; "action_clear" = "Wyczyść"; "action_close" = "Zamknij"; "action_complete_verification" = "Dokończ weryfikację"; "action_confirm" = "Potwierdź"; -"action_confirm_password" = "Confirm password"; +"action_confirm_password" = "Potwierdź hasło"; "action_continue" = "Kontynuuj"; "action_copy" = "Kopiuj"; "action_copy_link" = "Kopiuj link"; "action_copy_link_to_message" = "Kopiuj link do wiadomości"; "action_create" = "Utwórz"; "action_create_a_room" = "Utwórz pokój"; -"action_deactivate" = "Deactivate"; +"action_deactivate" = "Dezaktywuj"; +"action_deactivate_account" = "Dezaktywuj konto"; "action_decline" = "Odrzuć"; "action_delete_poll" = "Usuń ankietę"; "action_disable" = "Wyłącz"; @@ -64,6 +64,7 @@ "action_leave" = "Opuść"; "action_leave_conversation" = "Opuść rozmowę"; "action_leave_room" = "Opuść pokój"; +"action_load_more" = "Załaduj więcej"; "action_manage_account" = "Zarządzaj kontem"; "action_manage_devices" = "Zarządzaj urządzeniami"; "action_message" = "Wiadomość"; @@ -93,6 +94,7 @@ "action_send_message" = "Wyślij wiadomość"; "action_share" = "Udostępnij"; "action_share_link" = "Udostępnij link"; +"action_show" = "Show"; "action_sign_in_again" = "Zaloguj się ponownie"; "action_signout" = "Wyloguj"; "action_signout_anyway" = "Wyloguj mimo to"; @@ -105,17 +107,15 @@ "action_tap_for_options" = "Stuknij, by wyświetlić opcje"; "action_try_again" = "Spróbuj ponownie"; "action_unpin" = "Odepnij"; -"action_view_in_timeline" = "View in timeline"; +"action_view_in_timeline" = "Wyświetl na osi czasu"; "action_view_source" = "Wyświetl źródło"; "action_yes" = "Tak"; -"action.load_more" = "Załaduj więcej"; -"action_deactivate_account" = "Deactivate account"; -"banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; -"banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; -"banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; -"banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; -"banner.set_up_recovery.title" = "Set up recovery"; +"banner_migrate_to_native_sliding_sync_action" = "Wyloguj się i zaktualizuj"; +"banner_migrate_to_native_sliding_sync_description" = "Twój serwer obsługuje teraz nowy, szybszy protokół. Wyloguj się i zaloguj ponownie, aby uaktualnić teraz. Zrobienie tego teraz pomoże uniknąć wymuszonego wylogowania, gdy stary protokół zostanie później usunięty."; +"banner_migrate_to_native_sliding_sync_force_logout_title" = "Twój serwer domowy już nie wspiera starego protokołu. Zaloguj się ponownie, aby kontynuować korzystanie z aplikacji."; +"banner_migrate_to_native_sliding_sync_title" = "Dostępna aktualizacja"; +"banner.set_up_recovery.content" = "Wygeneruj nowy klucz przywracania, którego można użyć do przywrócenia historii wiadomości szyfrowanych w przypadku utraty dostępu do swoich urządzeń."; +"banner.set_up_recovery.title" = "Skonfiguruj przywracanie"; "common_about" = "O programie"; "common_acceptable_use_policy" = "Polityka użytkowania"; "common_advanced_settings" = "Ustawienia zaawansowane"; @@ -162,6 +162,7 @@ "common_modern" = "Nowoczesny"; "common_mute" = "Wycisz"; "common_no_results" = "Brak wyników"; +"common_no_room_name" = "Brak nazwy pokoju"; "common_offline" = "Offline"; "common_optic_id_ios" = "Optic ID"; "common_or" = "lub"; @@ -170,6 +171,8 @@ "common_permalink" = "Link bezpośredni"; "common_permission" = "Uprawnienie"; "common_please_wait" = "Proszę czekać..."; +"common_poll_end_confirmation" = "Jesteś pewien, że chcesz zakończyć tę ankietę?"; +"common_poll_summary" = "Ankieta: %1$@"; "common_poll_total_votes" = "Łączna liczba głosów: %1$@"; "common_poll_undisclosed_text" = "Wyniki zostaną wyświetlone po zakończeniu ankiety"; "common_privacy_policy" = "Polityka prywatności"; @@ -200,6 +203,7 @@ "common_settings" = "Ustawienia"; "common_shared_location" = "Udostępniona lokalizacja"; "common_signing_out" = "Wylogowywanie"; +"common_something_went_wrong" = "Coś poszło nie tak"; "common_starting_chat" = "Rozpoczynanie czatu…"; "common_sticker" = "Naklejka"; "common_success" = "Sukces"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "O czym jest ten pokój?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Nie można odszyfrować"; +"common_unable_to_decrypt_no_access" = "Nie masz uprawnień do tej wiadomości"; "common_unable_to_invite_message" = "Nie udało się wysłać zaproszenia do jednego lub więcej użytkowników."; "common_unable_to_invite_title" = "Nie można wysłać zaproszeń"; "common_unlock" = "Odblokuj"; @@ -221,24 +226,21 @@ "common_username" = "Nazwa użytkownika"; "common_verification_cancelled" = "Weryfikacja anulowana"; "common_verification_complete" = "Weryfikacja zakończona"; +"common_verify_device" = "Weryfikuj urządzenie"; "common_video" = "Film"; "common_voice_message" = "Wiadomość głosowa"; "common_waiting" = "Oczekiwanie…"; "common_waiting_for_decryption_key" = "Oczekiwanie na tę wiadomość"; +"common.copied_to_clipboard" = "Skopiowano do schowka"; "common.do_not_show_this_again" = "Nie pokazuj ponownie"; "common.open_source_licenses" = "Licencje open-source"; -"common.pinned" = "Pinned"; +"common.pinned" = "Przypięte"; "common.send_to" = "Wyślij do"; -"common.you" = "You"; -"common_no_room_name" = "Brak nazwy pokoju"; -"common_poll_end_confirmation" = "Jesteś pewien, że chcesz zakończyć tę ankietę?"; -"common_poll_summary" = "Ankieta: %1$@"; -"common_something_went_wrong" = "Coś poszło nie tak"; -"common_unable_to_decrypt_no_access" = "Nie masz uprawnień do tej wiadomości"; -"common_verify_device" = "Weryfikuj urządzenie"; +"common.you" = "Ty"; "confirm_recovery_key_banner_message" = "Twoja kopia zapasowa czatu jest obecnie niezsynchronizowana. Aby zachować dostęp do kopii zapasowej czatu, musisz potwierdzić klucz odzyskiwania."; "confirm_recovery_key_banner_title" = "Wprowadź swój klucz przywracania"; "crash_detection_dialog_content" = "%1$@ uległ awarii podczas ostatniego użycia. Czy chcesz przesłać nam raport o awarii?"; +"crypto_identity_change_pin_violation" = "Tożsamość %1$@ mogła ulec zmianie. %2$@"; "dialog_permission_camera" = "Aby umożliwić aplikacji korzystanie z aparatu, prosimy o udzielenie zezwolenia w ustawieniach systemowych."; "dialog_permission_generic" = "Proszę nadać uprawnienia w ustawieniach systemowych."; "dialog_permission_location_description_ios" = "Przyznaj dostęp w Ustawienia -> Lokalizacja."; @@ -259,7 +261,7 @@ "emoji_picker_category_people" = "Buźki i osoby"; "emoji_picker_category_places" = "Podróż i miejsca"; "emoji_picker_category_symbols" = "Symbole"; -"error_account_creation_not_possible" = "Your homeserver needs to be upgraded to support Matrix Authentication Service and account creation."; +"error_account_creation_not_possible" = "Twój serwer domowy wymaga aktualizacji, aby uzyskać wsparcie usługi Matrix Authentication Service i tworzenia kont."; "error_failed_creating_the_permalink" = "Nie udało się utworzyć linku bezpośredniego"; "error_failed_loading_map" = "%1$@ nie mogło wczytać mapy. Spróbuj ponownie później."; "error_failed_loading_messages" = "Nie udało się załadować wiadomości"; @@ -270,8 +272,8 @@ "error_some_messages_have_not_been_sent" = "Niektóre wiadomości nie zostały wysłane"; "error_unknown" = "Przepraszamy, wystąpił błąd"; "event_shield_reason_authenticity_not_guaranteed" = "Autentyczność tej wiadomości szyfrowanej nie jest gwarantowana na tym urządzeniu."; -"event_shield_reason_previously_verified" = "Encrypted by a previously-verified user."; -"event_shield_reason_sent_in_clear" = "Not encrypted."; +"event_shield_reason_previously_verified" = "Zaszyfrowane przez wcześniej zweryfikowanego użytkownika."; +"event_shield_reason_sent_in_clear" = "Nieszyfrowany."; "event_shield_reason_unknown_device" = "Zaszyfrowana przez nieznane lub usunięte urządzenie."; "event_shield_reason_unsigned_device" = "Zaszyfrowana przez urządzenie niezweryfikowane przez jego właściciela."; "event_shield_reason_unverified_identity" = "Zaszyfrowana przez niezweryfikowanego użytkownika."; @@ -291,16 +293,15 @@ "notification_channel_silent" = "Ciche powiadomienia"; "notification_incoming_call" = "Przychodzące połączenie"; "notification_inline_reply_failed" = "** Nie udało się wysłać - proszę otworzyć pokój"; -"notification_invitation_action_reject" = "Odrzuć"; "notification_invite_body" = "Zaprosił(a) cię do czatu"; -"notification_invite_body_with_sender" = "%1$@ invited you to chat"; +"notification_invite_body_with_sender" = "%1$@ zaprosił Cię do czatu"; "notification_mentioned_you_body" = "Wspomniano o Tobie: %1$@"; "notification_new_messages" = "Nowe wiadomości"; "notification_reaction_body" = "Zareagował z %1$@"; "notification_room_invite_body" = "Zaprosił Cię do dołączenia do pokoju"; -"notification_room_invite_body_with_sender" = "%1$@ invited you to join the room"; +"notification_room_invite_body_with_sender" = "%1$@ zaprosił Cię do pokoju"; "notification_sender_me" = "Ja"; -"notification_sender_mention_reply" = "%1$@ mentioned or replied"; +"notification_sender_mention_reply" = "%1$@ wspomniał lub odpowiedział"; "notification_test_push_notification_content" = "Wyświetlasz powiadomienie! Kliknij mnie!"; "notification_ticker_text_dm" = "%1$@: %2$@"; "notification_ticker_text_group" = "%1$@: %2$@ %3$@"; @@ -333,28 +334,26 @@ "screen_advanced_settings_element_call_base_url" = "Własny bazowy URL dla połączeń Element"; "screen_advanced_settings_element_call_base_url_description" = "Ustaw własny bazowy URL dla połączeń Element"; "screen_advanced_settings_element_call_base_url_validation_error" = "Nieprawidłowy adres URL, upewnij się, że zawiera protokół (http/https) i poprawny adres."; -"screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; -"screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; -"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; -"screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; -"screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; -"screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; -"screen_resolve_send_failure_changed_identity_title" = "Your message was not sent because %1$@’s verified identity has changed"; -"screen_resolve_send_failure_unsigned_device_primary_button_title" = "Send message anyway"; -"screen_resolve_send_failure_unsigned_device_subtitle" = "%1$@ is using one or more unverified devices. You can send the message anyway, or you can cancel for now and try again later after %2$@ has verified all their devices."; -"screen_resolve_send_failure_unsigned_device_title" = "Your message was not sent because %1$@ has not verified all devices"; -"screen_resolve_send_failure_you_unsigned_device_subtitle" = "One or more of your devices are unverified. You can send the message anyway, or you can cancel for now and try again later after you have verified all of your devices."; -"screen_resolve_send_failure_you_unsigned_device_title" = "Your message was not sent because you have not verified one or more of your devices"; +"screen_pinned_timeline_empty_state_description" = "Naciśnij wiadomość i wybierz “%1$@”, aby dołączyć tutaj."; +"screen_pinned_timeline_empty_state_headline" = "Przypinaj ważne wiadomości, aby można było je łatwo znaleźć"; +"screen_reset_encryption_password_error" = "Wystąpił nieznany błąd. Sprawdź, czy hasło jest poprawne i spróbuj ponownie."; +"screen_resolve_send_failure_changed_identity_primary_button_title" = "Wycofaj weryfikację i wyślij"; +"screen_resolve_send_failure_changed_identity_subtitle" = "Możesz wycofać weryfikację i wysłać wiadomość mimo wszystko lub anulować i spróbować ponownie po ponownej weryfikacji %1$@."; +"screen_resolve_send_failure_changed_identity_title" = "Twoja wiadomość nie została wysłana, ponieważ tożsamość %1$@ uległa zmianie."; +"screen_resolve_send_failure_unsigned_device_primary_button_title" = "Wyślij wiadomość mimo to"; +"screen_resolve_send_failure_unsigned_device_subtitle" = "%1$@ korzysta z jednego lub więcej niezweryfikowanych urządzeń. Wyślij wiadomość mimo to lub anuluj i spróbuj ponownie, gdy %2$@ zweryfikuje wszystkie swoje urządzenia."; +"screen_resolve_send_failure_unsigned_device_title" = "Twoja wiadomość nie została wysłana, ponieważ %1$@ nie zweryfikował swoich wszystkich urządzeń"; +"screen_resolve_send_failure_you_unsigned_device_subtitle" = "Jedno lub więcej z Twoich urządzeń jest niezweryfikowanych. Wyślij wiadomość mimo to lub anuluj i spróbuj ponownie po zweryfikowaniu wszystkich swoich urządzeń."; +"screen_resolve_send_failure_you_unsigned_device_title" = "Twoja wiadomość nie została wysłana, ponieważ nie zweryfikowałeś jednego lub więcej swoich urządzeń."; "screen_room_mentions_at_room_subtitle" = "Powiadom cały pokój"; "screen_room_pinned_banner_indicator" = "%1$@ z %2$@"; "screen_room_pinned_banner_indicator_description" = "%1$@ przypiętych wiadomości"; -"screen_room_pinned_banner_loading_description" = "Loading message…"; +"screen_room_pinned_banner_loading_description" = "Wczytywanie wiadomości..."; "screen_room_pinned_banner_view_all_button_title" = "Wyświetl wszystkie"; -"screen_room_details_pinned_events_row_title" = "Pinned messages"; -"screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; -"screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; -"screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "Zmień dostawcę konta"; +"screen_room_details_pinned_events_row_title" = "Przypięte wiadomości"; +"screen_timeline_item_menu_send_failure_changed_identity" = "Wiadomość nie została wysłana, ponieważ tożsamość %1$@ uległa zmianie."; +"screen_timeline_item_menu_send_failure_unsigned_device" = "Wiadomość nie została wysłana, ponieważ %1$@ nie zweryfikował wszystkich urządzeń."; +"screen_timeline_item_menu_send_failure_you_unsigned_device" = "Wiadomość nie została wysłana, ponieważ nie zweryfikowałeś jednego lub więcej swoich urządzeń."; "screen_account_provider_form_hint" = "Adres serwera domowego"; "screen_account_provider_form_notice" = "Wprowadź wyszukiwane hasło lub adres domeny."; "screen_account_provider_form_subtitle" = "Szukaj serwera firmowego, społeczności lub prywatnego."; @@ -432,11 +431,10 @@ "screen_chat_backup_key_backup_description" = "Backup zapewnia, że nie stracisz swojej historii wiadomości. %1$@"; "screen_chat_backup_key_backup_title" = "Backup"; "screen_chat_backup_recovery_action_change" = "Zmień klucz przywracania"; -"screen_chat_backup_recovery_action_confirm" = "Wprowadź klucz przywracania"; "screen_chat_backup_recovery_action_confirm_description" = "Backup czatu jest niezsynchronizowany."; "screen_chat_backup_recovery_action_setup" = "Skonfiguruj przywracanie"; "screen_chat_backup_recovery_action_setup_description" = "Uzyskaj dostęp do swoich wiadomości szyfrowanych, jeśli utracisz wszystkie swoje urządzenia lub zostaniesz wylogowany z %1$@."; -"screen_create_account_title" = "Create account"; +"screen_create_account_title" = "Utwórz konto"; "screen_create_new_recovery_key_list_item_1" = "Otwórz %1$@ na urządzeniu stacjonarnym"; "screen_create_new_recovery_key_list_item_2" = "Zaloguj się ponownie na swoje konto"; "screen_create_new_recovery_key_list_item_3" = "Gdy pojawi się prośba o weryfikację urządzenia, wybierz %1$@"; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Pokaż wyniki dopiero po zakończeniu ankiety"; "screen_create_poll_anonymous_headline" = "Ukryj głosy"; "screen_create_poll_answer_hint" = "Opcja %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Twoje zmiany nie zostaną zapisane"; "screen_create_poll_cancel_confirmation_title_ios" = "Anuluj ankietę"; "screen_create_poll_question_desc" = "Pytanie lub temat"; "screen_create_poll_question_hint" = "Czego dotyczy ankieta?"; @@ -460,17 +457,17 @@ "screen_create_room_public_option_description" = "Wiadomości nie są szyfrowane i każdy może je odczytać. Możesz aktywować szyfrowanie później."; "screen_create_room_public_option_title" = "Pokój publiczny (wszyscy)"; "screen_create_room_topic_label" = "Temat (opcjonalnie)"; -"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"; -"screen_deactivate_account_delete_all_messages_notice" = "Warning: Future users may see incomplete conversations."; -"screen_deactivate_account_description" = "Deactivating your account is %1$@, it will:"; -"screen_deactivate_account_description_bold_part" = "irreversible"; -"screen_deactivate_account_list_item_1" = "%1$@ your account (you can't log back in, and your ID can't be reused)."; -"screen_deactivate_account_list_item_1_bold_part" = "Permanently disable"; -"screen_deactivate_account_list_item_2" = "Remove you from all chat rooms."; -"screen_deactivate_account_list_item_3" = "Delete your account information from our identity server."; -"screen_deactivate_account_list_item_4" = "Your messages will still be visible to registered users but won’t be available to new or unregistered users if you choose to delete them."; -"screen_deactivate_account_title" = "Deactivate account"; +"screen_deactivate_account_confirmation_dialog_content" = "Potwierdź dezaktywacje konta. Tej akcji nie można cofnąć."; +"screen_deactivate_account_delete_all_messages" = "Usuń wszystkie moje wiadomości"; +"screen_deactivate_account_delete_all_messages_notice" = "Ostrzeżenie: Przyszli użytkownicy mogą zobaczyć niekompletne rozmowy."; +"screen_deactivate_account_description" = "Dezaktywacja konta jest %1$@, zostanie:"; +"screen_deactivate_account_description_bold_part" = "nieodwracalna"; +"screen_deactivate_account_list_item_1" = "%1$@ twoje konto (nie będziesz mógł się zalogować, a twoje ID przepadnie)."; +"screen_deactivate_account_list_item_1_bold_part" = "Permanentnie wyłączy"; +"screen_deactivate_account_list_item_2" = "Usunie Ciebie ze wszystkich pokoi rozmów."; +"screen_deactivate_account_list_item_3" = "Usunięte wszystkie dane konta z naszego serwera tożsamości."; +"screen_deactivate_account_list_item_4" = "Twoje wiadomości wciąż będą widoczne dla zarejestrowanych użytkowników, ale nie będą dostępne dla nowych lub niezarejestrowanych użytkowników, jeśli je usuniesz."; +"screen_deactivate_account_title" = "Dezaktywuj konto"; "screen_edit_poll_delete_confirmation" = "Czy na pewno chcesz usunąć tę ankietę?"; "screen_edit_profile_display_name" = "Wyświetlana nazwa"; "screen_edit_profile_display_name_placeholder" = "Twoja wyświetlana nazwa"; @@ -478,7 +475,7 @@ "screen_edit_profile_error_title" = "Nie można zaktualizować profilu"; "screen_edit_profile_title" = "Edytuj profil"; "screen_edit_profile_updating_details" = "Aktualizuję profil…"; -"screen_encryption_reset_action_continue_reset" = "Continue reset"; +"screen_encryption_reset_action_continue_reset" = "Kontynuuj resetowanie"; "screen_encryption_reset_bullet_1" = "Szczegóły konta, kontakty, preferencje i lista czatów zostaną zachowane"; "screen_encryption_reset_bullet_2" = "Utracisz istniejącą historię wiadomości"; "screen_encryption_reset_bullet_3" = "Wymagana będzie ponowna weryfikacja istniejących urządzeń i kontaktów"; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Czaty grupowe"; "screen_notification_settings_invite_for_me_label" = "Zaproszenia"; "screen_notification_settings_mentions_only_disclaimer" = "Twój serwer domowy nie wspiera tej opcji w pokojach szyfrowanych, możesz nie otrzymać powiadomień z niektórych pokoi."; -"screen_notification_settings_mentions_section_title" = "Wzmianki"; "screen_notification_settings_mode_all" = "Wszystkie"; "screen_notification_settings_mode_mentions" = "Wzmianki"; "screen_notification_settings_notification_section_title" = "Powiadamiaj mnie przez"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Wprowadź..."; "screen_recovery_key_confirm_lost_recovery_key" = "Zgubiłeś swój kod przywracania?"; "screen_recovery_key_confirm_success" = "Potwierdzono klucz przywracania"; -"screen_recovery_key_confirm_title" = "Wprowadź klucz przywracania"; "screen_recovery_key_copied_to_clipboard" = "Skopiowano klucz przywracania"; "screen_recovery_key_generating_key" = "Generuję..."; "screen_recovery_key_save_action" = "Zapisz klucz przywracania"; @@ -637,11 +632,10 @@ "screen_reset_encryption_confirmation_alert_action" = "Tak, zresetuj teraz"; "screen_reset_encryption_confirmation_alert_subtitle" = "Tego procesu nie można odwrócić."; "screen_reset_encryption_confirmation_alert_title" = "Czy na pewno chcesz zresetować szyfrowanie?"; -"screen_reset_encryption_password_placeholder" = "Wprowadź..."; "screen_reset_encryption_password_subtitle" = "Potwierdź, że chcesz zresetować szyfrowanie."; "screen_reset_encryption_password_title" = "Wprowadź hasło, aby kontynuować"; -"screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; -"screen_reset_identity_confirmation_title" = "Can't confirm? Go to your account to reset your identity."; +"screen_reset_identity_confirmation_subtitle" = "Zostaniesz przeniesiony na swoje konto %1$@, aby zresetować tożsamość. Wrócisz do aplikacji po zakończeniu."; +"screen_reset_identity_confirmation_title" = "Nie możesz potwierdzić? Przejdź do swojego konta i zresetuj swoją tożsamość."; "screen_room_alias_resolver_resolve_alias_failure" = "Nie udało się uzyskać aliasu pokoju."; "screen_room_attachment_source_camera" = "Kamera"; "screen_room_attachment_source_camera_video" = "Nagraj film"; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Administratorzy automatycznie mają uprawnienia moderatora"; "screen_room_change_role_moderators_title" = "Edytuj moderatorów"; "screen_room_change_role_unsaved_changes_description" = "Masz niezapisane zmiany."; -"screen_room_change_role_unsaved_changes_title" = "Zapisać zmiany?"; "screen_room_details_add_topic_title" = "Dodaj temat"; "screen_room_details_already_a_member" = "Jest już członkiem"; "screen_room_details_already_invited" = "Już zaproszony"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Nie udało się wyłączyć wyciszenia tego pokoju. Spróbuj ponownie."; "screen_room_details_notification_mode_custom" = "Niestandardowy"; "screen_room_details_notification_mode_default" = "Domyślny"; -"screen_room_details_notification_title" = "Powiadomienia"; "screen_room_details_share_room_title" = "Udostępnij pokój"; "screen_room_details_title" = "Informacje pokoju"; "screen_room_details_updating_room" = "Aktualizuję pokój…"; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Banowanie %1$@"; "screen_room_member_list_manage_member_ban" = "Usuń i zbanuj członka"; "screen_room_member_list_manage_member_remove" = "Usuń z pokoju"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Usuń i zbanuj członka"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Tylko usuń członka"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Usunąć członka i zablokować możliwość dołączenia w przyszłości?"; "screen_room_member_list_manage_member_unban_action" = "Odbanuj"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Oznacz jako przeczytane"; "screen_roomlist_mark_as_unread" = "Oznacz jako nieprzeczytane"; "screen_roomlist_room_directory_button_title" = "Przeglądaj wszystkie pokoje"; -"screen_server_confirmation_change_server" = "Zmień dostawcę konta"; "screen_server_confirmation_message_login_element_dot_io" = "Serwer prywatny dla pracowników Element."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix to otwarta sieć do bezpiecznej i zdecentralizowanej komunikacji."; "screen_server_confirmation_message_register" = "Tutaj będą przechowywane Twoje konwersacje - w podobnej formie jak wiadomości widnieją na skrzynce e-mail."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Zamierzasz wylogować się ze swojej ostatniej sesji. Jeśli wylogujesz się teraz, stracisz dostęp do swoich wiadomości szyfrowanych."; "screen_signout_key_backup_disabled_title" = "Wyłączyłeś backup"; "screen_signout_key_backup_offline_subtitle" = "Twoje klucze były nadal archiwizowane po przejściu w tryb offline. Połącz się ponownie, aby zapisać w chmurze przed wylogowaniem."; -"screen_signout_key_backup_offline_title" = "Twoje klucze są nadal archiwizowane"; "screen_signout_key_backup_ongoing_subtitle" = "Zanim się wylogujesz, poczekaj na zakończenie operacji."; "screen_signout_key_backup_ongoing_title" = "Twoje klucze są nadal archiwizowane"; "screen_signout_recovery_disabled_subtitle" = "Zamierzasz wylogować się ze swojej ostatniej sesji. Jeśli wylogujesz się teraz, stracisz dostęp do swoich wiadomości szyfrowanych."; "screen_signout_recovery_disabled_title" = "Nie ustawiono przywracania"; "screen_signout_save_recovery_key_subtitle" = "Zamierzasz wylogować się ze swojej ostatniej sesji. Jeśli wylogujesz się teraz, stracisz dostęp do swoich wiadomości szyfrowanych."; -"screen_signout_save_recovery_key_title" = "Czy zapisałeś swój klucz przywracania?"; "screen_start_chat_error_starting_chat" = "Wystąpił błąd podczas próby rozpoczęcia czatu"; "screen_view_location_title" = "Lokalizacja"; "screen_welcome_bullet_1" = "Połączenia, ankiety, wyszukiwanie i inne zostaną dodane później w tym roku."; @@ -920,7 +908,6 @@ "test_language_identifier" = "pl"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Rozwiązywanie problemów"; -"troubleshoot_notifications_entry_point_title" = "Powiadomienia rozwiązywania problemów"; "troubleshoot_notifications_screen_action" = "Uruchom testy"; "troubleshoot_notifications_screen_action_again" = "Uruchom testy ponownie"; "troubleshoot_notifications_screen_failure" = "Niektóre testy się nie powiodły. Sprawdź szczegóły."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Upewnij się, że dystrybutorzy UnifiedPush są dostępni."; "troubleshoot_notifications_test_unified_push_failure" = "Nie znaleziono dystrybutorów push."; "troubleshoot_notifications_test_unified_push_title" = "Sprawdź UnifiedPush"; +"a11y_poll" = "Ankieta"; "dialog_title_error" = "Błąd"; "dialog_title_success" = "Sukces"; "notification_fallback_content" = "Powiadomienie"; "notification_invitation_action_join" = "Dołącz"; +"notification_invitation_action_reject" = "Odrzuć"; "notification_room_action_mark_as_read" = "Oznacz jako przeczytane"; "notification_room_action_quick_reply" = "Szybka odpowiedź"; +"screen_pinned_timeline_screen_title_empty" = "Przypięte wiadomości"; "screen_room_mentions_at_room_title" = "Wszyscy"; +"screen_account_provider_change" = "Zmień dostawcę konta"; "screen_account_provider_signin_subtitle" = "Tutaj będą przechowywane Twoje konwersacje - w podobnej formie jak wiadomości widnieją na skrzynce e-mail."; "screen_account_provider_signup_subtitle" = "Tutaj będą przechowywane Twoje konwersacje - w podobnej formie jak wiadomości widnieją na skrzynce e-mail."; "screen_analytics_settings_help_us_improve" = "Udostępniaj anonimowe dane użytkowania, aby pomóc nam identyfikować problemy."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Będziesz mógł ponownie zobaczyć wszystkie wiadomości od tego użytkownika."; "screen_blocked_users_unblock_alert_title" = "Odblokuj użytkownika"; "screen_bug_report_rash_logs_alert_title" = "%1$@ uległ awarii podczas ostatniego użycia. Czy chcesz przesłać nam raport o awarii?"; +"screen_chat_backup_recovery_action_confirm" = "Wprowadź klucz przywracania"; +"screen_create_poll_cancel_confirmation_content_ios" = "Zmiany nie zostaną zapisane"; "screen_create_room_add_people_title" = "Zaproś znajomych"; "screen_create_room_room_name_label" = "Nazwa pokoju"; "screen_create_room_title" = "Utwórz pokój"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Edytuj ankietę"; "screen_identity_use_another_device" = "Użyj innego urządzenia"; "screen_login_subtitle" = "Matrix to otwarta sieć do bezpiecznej i zdecentralizowanej komunikacji."; +"screen_notification_settings_mentions_section_title" = "Wzmianki"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Spróbuj ponownie"; +"screen_recovery_key_confirm_title" = "Wprowadź swój klucz przywracania"; "screen_report_content_block_user" = "Zablokuj użytkownika"; +"screen_reset_encryption_password_placeholder" = "Wprowadź..."; "screen_room_attachment_source_camera_photo" = "Zrób zdjęcie"; "screen_room_change_permissions_everyone" = "Wszyscy"; "screen_room_change_permissions_member_moderation" = "Moderacja członków"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Administratorzy"; "screen_room_change_role_section_moderators" = "Moderatorzy"; "screen_room_change_role_section_users" = "Członków"; +"screen_room_change_role_unsaved_changes_title" = "Zapisać zmiany?"; "screen_room_details_invite_people_title" = "Zaproś znajomych"; "screen_room_details_leave_conversation_title" = "Opuść rozmowę"; "screen_room_details_leave_room_title" = "Opuść pokój"; +"screen_room_details_notification_title" = "Powiadomienia"; "screen_room_details_roles_and_permissions" = "Role i uprawnienia"; "screen_room_details_room_name_label" = "Nazwa pokoju"; "screen_room_details_security_title" = "Bezpieczeństwo"; "screen_room_details_topic_title" = "Temat"; "screen_room_error_failed_processing_media" = "Przetwarzanie multimediów do przesłania nie powiodło się, spróbuj ponownie."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Usuń i zbanuj członka"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Tylko wzmianki i słowa kluczowe"; "screen_roomlist_filter_people" = "Osoby"; +"screen_server_confirmation_change_server" = "Zmień dostawcę konta"; "screen_signout_confirmation_dialog_submit" = "Wyloguj"; "screen_signout_confirmation_dialog_title" = "Wyloguj"; +"screen_signout_key_backup_offline_title" = "Twoje klucze są nadal archiwizowane"; "screen_signout_preference_item" = "Wyloguj"; +"screen_signout_save_recovery_key_title" = "Czy zapisałeś swój klucz przywracania?"; +"troubleshoot_notifications_entry_point_title" = "Powiadomienia rozwiązywania problemów"; diff --git a/ElementX/Resources/Localizations/pl.lproj/Localizable.stringsdict b/ElementX/Resources/Localizations/pl.lproj/Localizable.stringsdict index fdf4328867..794f787cf5 100644 --- a/ElementX/Resources/Localizations/pl.lproj/Localizable.stringsdict +++ b/ElementX/Resources/Localizations/pl.lproj/Localizable.stringsdict @@ -229,9 +229,11 @@ NSStringFormatValueTypeKey d one - %1$d Pinned message - other - %1$d Pinned messages + %1$d przypięta wiadomość + few + %1$d przypięte wiadomości + many + %1$d przypiętych wiadomości screen_room_member_list_header_title diff --git a/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings b/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings index 163228042b..7435f5a751 100644 --- a/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Pausar"; "a11y_pin_field" = "Campo de PIN"; "a11y_play" = "Reproduzir"; -"a11y_poll" = "Enquete"; "a11y_poll_end" = "Enquete encerrada"; "a11y_react_with" = "Reagir com %1$@"; "a11y_react_with_other_emojis" = "Reaja com outros emojis"; @@ -41,6 +40,7 @@ "action_create" = "Criar"; "action_create_a_room" = "Criar uma sala"; "action_deactivate" = "Deactivate"; +"action_deactivate_account" = "Deactivate account"; "action_decline" = "Recusar"; "action_delete_poll" = "Excluir Enquete"; "action_disable" = "Desabilitar"; @@ -64,6 +64,7 @@ "action_leave" = "Sair"; "action_leave_conversation" = "Sair da conversa"; "action_leave_room" = "Sair da sala"; +"action_load_more" = "Carregar mais"; "action_manage_account" = "Gerenciar conta"; "action_manage_devices" = "Gerenciar dispositivos"; "action_message" = "Mensagem"; @@ -93,6 +94,7 @@ "action_send_message" = "Enviar mensagem"; "action_share" = "Compartilhar"; "action_share_link" = "Compartilhar link"; +"action_show" = "Show"; "action_sign_in_again" = "Iniciar sessão novamente"; "action_signout" = "Sair"; "action_signout_anyway" = "Sair mesmo assim"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "View in timeline"; "action_view_source" = "Ver fonte"; "action_yes" = "Sim"; -"action.load_more" = "Carregar mais"; -"action_deactivate_account" = "Deactivate account"; "banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; @@ -162,6 +162,7 @@ "common_modern" = "Moderno"; "common_mute" = "Silenciar"; "common_no_results" = "Sem resultados"; +"common_no_room_name" = "Sem nome de sala"; "common_offline" = "Offline"; "common_optic_id_ios" = "ID ótico"; "common_or" = "ou"; @@ -170,6 +171,8 @@ "common_permalink" = "Link permanente"; "common_permission" = "Permissão"; "common_please_wait" = "Por favor, aguarde..."; +"common_poll_end_confirmation" = "Tem certeza de que deseja encerrar esta enquete?"; +"common_poll_summary" = "Enquete: %1$@"; "common_poll_total_votes" = "Total de votos: %1$@"; "common_poll_undisclosed_text" = "Os resultados serão exibidos após o término da enquete"; "common_privacy_policy" = "Política de Privacidade"; @@ -200,6 +203,7 @@ "common_settings" = "Configurações"; "common_shared_location" = "Localização compartilhada"; "common_signing_out" = "Saindo"; +"common_something_went_wrong" = "Algo deu errado"; "common_starting_chat" = "Iniciando o chat..."; "common_sticker" = "Adesivo"; "common_success" = "Sucesso"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "Sobre o que é essa sala?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Não é possível descriptografar"; +"common_unable_to_decrypt_no_access" = "Você não tem acesso a esta mensagem"; "common_unable_to_invite_message" = "Não foi possível enviar convites para um ou mais usuários."; "common_unable_to_invite_title" = "Não foi possível enviar o(s) convite(s)"; "common_unlock" = "Desbloquear"; @@ -221,24 +226,21 @@ "common_username" = "Nome do usuário"; "common_verification_cancelled" = "Verificação cancelada"; "common_verification_complete" = "Verificação concluída"; +"common_verify_device" = "Verificar dispositivo"; "common_video" = "Vídeo"; "common_voice_message" = "Mensagem de voz"; "common_waiting" = "Esperando..."; "common_waiting_for_decryption_key" = "Aguardando esta mensagem"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "Não mostrar isto novamente"; "common.open_source_licenses" = "Licenças de código aberto"; "common.pinned" = "Pinned"; "common.send_to" = "Enviar para"; "common.you" = "You"; -"common_no_room_name" = "Sem nome de sala"; -"common_poll_end_confirmation" = "Tem certeza de que deseja encerrar esta enquete?"; -"common_poll_summary" = "Enquete: %1$@"; -"common_something_went_wrong" = "Algo deu errado"; -"common_unable_to_decrypt_no_access" = "Você não tem acesso a esta mensagem"; -"common_verify_device" = "Verificar dispositivo"; "confirm_recovery_key_banner_message" = "Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup."; "confirm_recovery_key_banner_title" = "Insira sua chave de recuperação"; "crash_detection_dialog_content" = "%1$@ fechou inesperadamente na última vez que foi usado. Gostaria de compartilhar um relatório de falhas conosco?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "Para permitir que o aplicativo use a câmera, conceda a permissão nas configurações do sistema."; "dialog_permission_generic" = "Por favor, conceda a permissão nas configurações do sistema."; "dialog_permission_location_description_ios" = "Permita o acesso em Configurações -> Localização."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "Notificações silenciosas"; "notification_incoming_call" = "Incoming call"; "notification_inline_reply_failed" = "** Falha ao enviar - por favor, abra a sala"; -"notification_invitation_action_reject" = "Rejeitar"; "notification_invite_body" = "Convidou você para conversar"; "notification_invite_body_with_sender" = "%1$@ invited you to chat"; "notification_mentioned_you_body" = "Mencionou você: %1$@"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "URL inválida, por favor verifique se o protocolo (http/https) e o endereço correto estão presentes."; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; -"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; "screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "Alterar provedor da conta"; "screen_account_provider_form_hint" = "Endereço do servidor"; "screen_account_provider_form_notice" = "Insira um termo de pesquisa ou um endereço de domínio."; "screen_account_provider_form_subtitle" = "Procure uma empresa, comunidade ou servidor privado."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "O backup garante que você não perca seu histórico de mensagens. %1$@."; "screen_chat_backup_key_backup_title" = "Backup"; "screen_chat_backup_recovery_action_change" = "Alterar chave de recuperação"; -"screen_chat_backup_recovery_action_confirm" = "Insira a chave de recuperação"; "screen_chat_backup_recovery_action_confirm_description" = "Seu backup das conversas está atualmente fora de sincronia."; "screen_chat_backup_recovery_action_setup" = "Configurar a recuperação"; "screen_chat_backup_recovery_action_setup_description" = "Tenha acesso às suas mensagens criptografadas se você perder todos os seus dispositivos ou for desconectado do %1$@ em qualquer lugar."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Mostrar resultados somente após o término da enquete"; "screen_create_poll_anonymous_headline" = "Ocultar votos"; "screen_create_poll_answer_hint" = "Opção %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Suas alterações não serão salvas"; "screen_create_poll_cancel_confirmation_title_ios" = "Cancelar enquete"; "screen_create_poll_question_desc" = "Pergunta ou tópico"; "screen_create_poll_question_hint" = "Sobre o que é a enquete?"; @@ -480,7 +477,7 @@ "screen_edit_profile_updating_details" = "Atualizando o perfil..."; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history"; +"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Bate-papos em grupo"; "screen_notification_settings_invite_for_me_label" = "Convites"; "screen_notification_settings_mentions_only_disclaimer" = "Your homeserver does not support this option in encrypted rooms, you may not get notified in some rooms."; -"screen_notification_settings_mentions_section_title" = "Menções"; "screen_notification_settings_mode_all" = "Todos"; "screen_notification_settings_mode_mentions" = "Menções"; "screen_notification_settings_notification_section_title" = "Me notifique para"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Inserir..."; "screen_recovery_key_confirm_lost_recovery_key" = "Lost your recovery key?"; "screen_recovery_key_confirm_success" = "Chave de recuperação confirmada"; -"screen_recovery_key_confirm_title" = "Insira sua chave de recuperação"; "screen_recovery_key_copied_to_clipboard" = "Chave de recuperação copiada"; "screen_recovery_key_generating_key" = "Gerando..."; "screen_recovery_key_save_action" = "Salvar chave de recuperação"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Yes, reset now"; "screen_reset_encryption_confirmation_alert_subtitle" = "This process is irreversible."; "screen_reset_encryption_confirmation_alert_title" = "Are you sure you want to reset your identity?"; -"screen_reset_encryption_password_placeholder" = "Enter…"; "screen_reset_encryption_password_subtitle" = "Confirm that you want to reset your identity."; "screen_reset_encryption_password_title" = "Enter your account password to continue"; "screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Admins automatically have moderator privileges"; "screen_room_change_role_moderators_title" = "Editar moderadores"; "screen_room_change_role_unsaved_changes_description" = "Você tem alterações não salvas."; -"screen_room_change_role_unsaved_changes_title" = "Salvar alterações?"; "screen_room_details_add_topic_title" = "Adicionar tópico"; "screen_room_details_already_a_member" = "Já é membro"; "screen_room_details_already_invited" = "Já foi convidado"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Falha ao ativar o som desta sala. Tente novamente."; "screen_room_details_notification_mode_custom" = "Personalizado"; "screen_room_details_notification_mode_default" = "Padrão"; -"screen_room_details_notification_title" = "Notificações"; "screen_room_details_share_room_title" = "Compartilhar sala"; "screen_room_details_title" = "Room info"; "screen_room_details_updating_room" = "Atualizando a sala..."; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Banindo %1$@"; "screen_room_member_list_manage_member_ban" = "Remover e banir membro"; "screen_room_member_list_manage_member_remove" = "Remover da sala"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Remover e banir membro"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Somente remover membro"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Remover membro e banir de entrar novamente no futuro?"; "screen_room_member_list_manage_member_unban_action" = "Desbanir"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Marcar como lido"; "screen_roomlist_mark_as_unread" = "Marcar como não lido"; "screen_roomlist_room_directory_button_title" = "Navegar por todas as salas"; -"screen_server_confirmation_change_server" = "Alterar provedor da conta"; "screen_server_confirmation_message_login_element_dot_io" = "Um servidor privado para funcionários do Element."; "screen_server_confirmation_message_login_matrix_dot_org" = "A Matrix é uma rede aberta para comunicação segura e descentralizada."; "screen_server_confirmation_message_register" = "Aqui é onde suas conversas vão ficar — assim como você usa um provedor de e-mails para manter seus e-mails."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "You are about to sign out of your last session. If you sign out now, you will lose access to your encrypted messages."; "screen_signout_key_backup_disabled_title" = "Você desativou o backup"; "screen_signout_key_backup_offline_subtitle" = "Your keys were still being backed up when you went offline. Reconnect so that your keys can be backed up before signing out."; -"screen_signout_key_backup_offline_title" = "Your keys are still being backed up"; "screen_signout_key_backup_ongoing_subtitle" = "Please wait for this to complete before signing out."; "screen_signout_key_backup_ongoing_title" = "O backup das suas chaves ainda está em andamento"; "screen_signout_recovery_disabled_subtitle" = "You are about to sign out of your last session. If you sign out now, you'll lose access to your encrypted messages."; "screen_signout_recovery_disabled_title" = "A recuperação não está configurada"; "screen_signout_save_recovery_key_subtitle" = "You are about to sign out of your last session. If you sign out now, you might lose access to your encrypted messages."; -"screen_signout_save_recovery_key_title" = "Você salvou sua chave de recuperação?"; "screen_start_chat_error_starting_chat" = "Ocorreu um erro ao tentar iniciar um chat"; "screen_view_location_title" = "Localização"; "screen_welcome_bullet_1" = "Chamadas, enquetes, pesquisa e muito mais serão adicionadas ainda este ano."; @@ -920,7 +908,6 @@ "test_language_identifier" = "pt-br"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Troubleshoot"; -"troubleshoot_notifications_entry_point_title" = "Troubleshoot notifications"; "troubleshoot_notifications_screen_action" = "Execute testes"; "troubleshoot_notifications_screen_action_again" = "Run tests again"; "troubleshoot_notifications_screen_failure" = "Some tests failed. Please check the details."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Ensure that UnifiedPush distributors are available."; "troubleshoot_notifications_test_unified_push_failure" = "No push distributors found."; "troubleshoot_notifications_test_unified_push_title" = "Check UnifiedPush"; +"a11y_poll" = "Enquete"; "dialog_title_error" = "Erro"; "dialog_title_success" = "Sucesso"; "notification_fallback_content" = "Notificação"; "notification_invitation_action_join" = "Entrar"; +"notification_invitation_action_reject" = "Recusar"; "notification_room_action_mark_as_read" = "Marcar como lido"; "notification_room_action_quick_reply" = "Resposta rápida"; +"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_room_mentions_at_room_title" = "Todos"; +"screen_account_provider_change" = "Alterar provedor da conta"; "screen_account_provider_signin_subtitle" = "Aqui é onde suas conversas vão ficar — assim como você usa um provedor de e-mails para manter seus e-mails."; "screen_account_provider_signup_subtitle" = "Aqui é onde suas conversas vão ficar — assim como você usa um provedor de e-mails para manter seus e-mails."; "screen_analytics_settings_help_us_improve" = "Compartilhe dados de uso anônimos para nos ajudar a identificar problemas."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Você poderá ver todas as mensagens deles novamente."; "screen_blocked_users_unblock_alert_title" = "Desbloquear usuário"; "screen_bug_report_rash_logs_alert_title" = "%1$@ fechou inesperadamente na última vez que foi usado. Gostaria de compartilhar um relatório de falhas conosco?"; +"screen_chat_backup_recovery_action_confirm" = "Insira a chave de recuperação"; +"screen_create_poll_cancel_confirmation_content_ios" = "Suas alterações não serão salvas"; "screen_create_room_add_people_title" = "Convidar pessoas"; "screen_create_room_room_name_label" = "Nome da sala"; "screen_create_room_title" = "Criar uma sala"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Editar enquete"; "screen_identity_use_another_device" = "Usar outro dispositivo"; "screen_login_subtitle" = "A Matrix é uma rede aberta para comunicação segura e descentralizada."; +"screen_notification_settings_mentions_section_title" = "Menções"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Tente novamente"; +"screen_recovery_key_confirm_title" = "Insira sua chave de recuperação"; "screen_report_content_block_user" = "Bloquear usuário"; +"screen_reset_encryption_password_placeholder" = "Inserir..."; "screen_room_attachment_source_camera_photo" = "Tirar foto"; "screen_room_change_permissions_everyone" = "Todos"; "screen_room_change_permissions_member_moderation" = "Moderação de membros"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Administradores"; "screen_room_change_role_section_moderators" = "Moderadores"; "screen_room_change_role_section_users" = "Membros"; +"screen_room_change_role_unsaved_changes_title" = "Salvar alterações?"; "screen_room_details_invite_people_title" = "Convidar pessoas"; "screen_room_details_leave_conversation_title" = "Sair da conversa"; "screen_room_details_leave_room_title" = "Sair da sala"; +"screen_room_details_notification_title" = "Notificações"; "screen_room_details_roles_and_permissions" = "Cargos e permissões"; "screen_room_details_room_name_label" = "Nome da sala"; "screen_room_details_security_title" = "Segurança"; "screen_room_details_topic_title" = "Tópico"; "screen_room_error_failed_processing_media" = "Falha ao processar mídia para upload. Tente novamente."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Remover e banir membro"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Somente menções e palavras-chave"; "screen_roomlist_filter_people" = "Pessoas"; +"screen_server_confirmation_change_server" = "Alterar provedor da conta"; "screen_signout_confirmation_dialog_submit" = "Sair"; "screen_signout_confirmation_dialog_title" = "Sair"; +"screen_signout_key_backup_offline_title" = "O backup das suas chaves ainda está em andamento"; "screen_signout_preference_item" = "Sair"; +"screen_signout_save_recovery_key_title" = "Você salvou sua chave de recuperação?"; +"troubleshoot_notifications_entry_point_title" = "Troubleshoot notifications"; diff --git a/ElementX/Resources/Localizations/pt.lproj/Localizable.strings b/ElementX/Resources/Localizations/pt.lproj/Localizable.strings index b7bc7ca582..de2820b436 100644 --- a/ElementX/Resources/Localizations/pt.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pt.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Pausar"; "a11y_pin_field" = "Campo para PIN"; "a11y_play" = "Reproduzir"; -"a11y_poll" = "Sondagem"; "a11y_poll_end" = "Sondagem concluída"; "a11y_react_with" = "Reagir com %1$@"; "a11y_react_with_other_emojis" = "Reagir com outros emojis"; @@ -27,20 +26,21 @@ "action_back" = "Voltar"; "action_call" = "Chamar"; "action_cancel" = "Cancelar"; -"action_cancel_for_now" = "Cancel for now"; +"action_cancel_for_now" = "Cancelar por enquanto"; "action_choose_photo" = "Escolher foto"; "action_clear" = "Limpar"; "action_close" = "Fechar"; "action_complete_verification" = "Concluir verificação"; "action_confirm" = "Confirmar"; -"action_confirm_password" = "Confirm password"; +"action_confirm_password" = "Confirmar palavra-passe"; "action_continue" = "Continuar"; "action_copy" = "Copiar"; "action_copy_link" = "Copiar ligação"; "action_copy_link_to_message" = "Copiar ligação da mensagem"; "action_create" = "Criar"; "action_create_a_room" = "Criar uma sala"; -"action_deactivate" = "Deactivate"; +"action_deactivate" = "Desativar"; +"action_deactivate_account" = "Desativar conta"; "action_decline" = "Rejeitar"; "action_delete_poll" = "Eliminar sondagem"; "action_disable" = "Desativar"; @@ -64,6 +64,7 @@ "action_leave" = "Sair"; "action_leave_conversation" = "Sair da conversa"; "action_leave_room" = "Sair da sala"; +"action_load_more" = "Carrega mais"; "action_manage_account" = "Gerir conta"; "action_manage_devices" = "Gerir dispositivos"; "action_message" = "Enviar mensagem"; @@ -93,6 +94,7 @@ "action_send_message" = "Enviar mensagem"; "action_share" = "Partilhar"; "action_share_link" = "Partilhar ligação"; +"action_show" = "Mostrar"; "action_sign_in_again" = "Iniciar sessão novamente"; "action_signout" = "Terminar sessão"; "action_signout_anyway" = "Terminar mesmo assim"; @@ -105,17 +107,15 @@ "action_tap_for_options" = "Toca para ver as opções"; "action_try_again" = "Tentar novamente"; "action_unpin" = "Desafixar"; -"action_view_in_timeline" = "View in timeline"; +"action_view_in_timeline" = "Ver na cronologia"; "action_view_source" = "Ver fonte"; "action_yes" = "Sim"; -"action.load_more" = "Carrega mais"; -"action_deactivate_account" = "Deactivate account"; -"banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; -"banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; -"banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; -"banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; -"banner.set_up_recovery.title" = "Set up recovery"; +"banner_migrate_to_native_sliding_sync_action" = "Sair & Atualizar"; +"banner_migrate_to_native_sliding_sync_description" = "O teu servidor suporta agora um protocolo novo e mais rápido. Termina a sessão e volta a iniciar sessão para atualizar agora. Se o fizeres agora, evitarás um fim de sessão forçado quando o protocolo antigo for removido mais tarde."; +"banner_migrate_to_native_sliding_sync_force_logout_title" = "Seu homeserver não suporta mais o protocolo antigo. Termine sessão e volte a iniciar sessão para continuar a utilizar a aplicação."; +"banner_migrate_to_native_sliding_sync_title" = "Atualização disponível"; +"banner.set_up_recovery.content" = "Gere uma nova chave de recuperação que pode ser usada para restaurar seu histórico de mensagens criptografadas caso você perca o acesso aos seus dispositivos."; +"banner.set_up_recovery.title" = "Configurar a recuperação"; "common_about" = "Sobre"; "common_acceptable_use_policy" = "Política de utilização aceitável"; "common_advanced_settings" = "Configurações avançadas"; @@ -162,6 +162,7 @@ "common_modern" = "Moderno"; "common_mute" = "Silenciar"; "common_no_results" = "Sem resultados"; +"common_no_room_name" = "Sala sem nome"; "common_offline" = "Desligado"; "common_optic_id_ios" = "Optic ID"; "common_or" = "ou"; @@ -170,6 +171,8 @@ "common_permalink" = "Ligação permanente"; "common_permission" = "Permissão"; "common_please_wait" = "Por favor, aguarda…"; +"common_poll_end_confirmation" = "Tens a certeza que queres concluir esta sondagem?"; +"common_poll_summary" = "Sondagem: %1$@"; "common_poll_total_votes" = "Total de votos: %1$@"; "common_poll_undisclosed_text" = "Os resultados serão apresentados após o fim da sondagem"; "common_privacy_policy" = "Política de privacidade"; @@ -200,6 +203,7 @@ "common_settings" = "Configurações"; "common_shared_location" = "Localização partilhada"; "common_signing_out" = "A terminar sessão"; +"common_something_went_wrong" = "Algo correu mal"; "common_starting_chat" = "A iniciar conversa…"; "common_sticker" = "Autocolante"; "common_success" = "Sucesso"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "Sobre o que é esta sala?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Incapaz de decifrar"; +"common_unable_to_decrypt_no_access" = "Não tens acesso a esta mensagem"; "common_unable_to_invite_message" = "Não foi possível enviar convites a um ou mais utilizadores."; "common_unable_to_invite_title" = "Não foi possível enviar convite(s)"; "common_unlock" = "Desbloquear"; @@ -221,24 +226,21 @@ "common_username" = "Nome de utilizador"; "common_verification_cancelled" = "Verificação cancelada"; "common_verification_complete" = "Verificação concluída"; +"common_verify_device" = "Verificar o dispositivo"; "common_video" = "Vídeo"; "common_voice_message" = "Mensagem de voz"; "common_waiting" = "A aguardar…"; "common_waiting_for_decryption_key" = "À espera desta mensagem"; +"common.copied_to_clipboard" = "Copiado para a área de transferência"; "common.do_not_show_this_again" = "Não mostrar novamente"; "common.open_source_licenses" = "Licenças de código aberto"; -"common.pinned" = "Pinned"; +"common.pinned" = "Afixado"; "common.send_to" = "Enviar para"; -"common.you" = "You"; -"common_no_room_name" = "Sala sem nome"; -"common_poll_end_confirmation" = "Tens a certeza que queres concluir esta sondagem?"; -"common_poll_summary" = "Sondagem: %1$@"; -"common_something_went_wrong" = "Algo correu mal"; -"common_unable_to_decrypt_no_access" = "Não tens acesso a esta mensagem"; -"common_verify_device" = "Verificar o dispositivo"; +"common.you" = "Você"; "confirm_recovery_key_banner_message" = "A tua cópia de segurança das conversas está atualmente dessincronizada. Tens de inserir a tua chave de recuperação para manteres o acesso à cópia."; "confirm_recovery_key_banner_title" = "Insere a tua chave de recuperação"; "crash_detection_dialog_content" = "A %1$@ teve uma falha da última vez que foi utilizada. Gostarias de partilhar um relatório de acidente connosco?"; +"crypto_identity_change_pin_violation" = "A identidade de %1$@ parece ter mudado. %2$@"; "dialog_permission_camera" = "Para que a aplicação possa utilizar a câmara, concede a permissão nas configurações do sistema."; "dialog_permission_generic" = "Concede a permissão nas configurações do sistema."; "dialog_permission_location_description_ios" = "Concede a permissão em Definições -> Localização."; @@ -259,7 +261,7 @@ "emoji_picker_category_people" = "Caras e Pessoas"; "emoji_picker_category_places" = "Viagens e Lugares"; "emoji_picker_category_symbols" = "Símbolos"; -"error_account_creation_not_possible" = "Your homeserver needs to be upgraded to support Matrix Authentication Service and account creation."; +"error_account_creation_not_possible" = "Seu homeserver precisa ser atualizado para suportar o Matrix Authentication Service e a criação de conta."; "error_failed_creating_the_permalink" = "Falha ao criar ligação permanente"; "error_failed_loading_map" = "%1$@ não foi possível carregar o mapa. Por favor, tente novamente mais tarde."; "error_failed_loading_messages" = "Falha ao carregar mensagens"; @@ -270,8 +272,8 @@ "error_some_messages_have_not_been_sent" = "Algumas mensagens não foram enviadas"; "error_unknown" = "Ocorreu um erro, desculpa"; "event_shield_reason_authenticity_not_guaranteed" = "A autenticidade desta mensagem cifrada não pode ser garantida neste dispositivo."; -"event_shield_reason_previously_verified" = "Encrypted by a previously-verified user."; -"event_shield_reason_sent_in_clear" = "Not encrypted."; +"event_shield_reason_previously_verified" = "Criptografado por um usuário verificado anteriormente."; +"event_shield_reason_sent_in_clear" = "Não encriptado."; "event_shield_reason_unknown_device" = "Cifragem com origem num dispositivo eliminado ou desconhecido."; "event_shield_reason_unsigned_device" = "Cifragem com origem num dispositivo não verificado pelo seu dono."; "event_shield_reason_unverified_identity" = "Cifragem com origem num utilizador não verificado."; @@ -291,16 +293,15 @@ "notification_channel_silent" = "Notificações silenciosas"; "notification_incoming_call" = "Chamada recebida"; "notification_inline_reply_failed" = "** Falha no envio - por favor abre a sala"; -"notification_invitation_action_reject" = "Rejeitar"; "notification_invite_body" = "Convidou-te para conversar"; -"notification_invite_body_with_sender" = "%1$@ invited you to chat"; +"notification_invite_body_with_sender" = "%1$@ convidou-o para conversar"; "notification_mentioned_you_body" = "Mencionou-te: %1$@"; "notification_new_messages" = "Mensagens novas"; "notification_reaction_body" = "Reagiu com %1$@"; "notification_room_invite_body" = "Convidou-te a entrar na sala"; -"notification_room_invite_body_with_sender" = "%1$@ invited you to join the room"; +"notification_room_invite_body_with_sender" = "%1$@ convidou-o a juntar-se à sala"; "notification_sender_me" = "Eu"; -"notification_sender_mention_reply" = "%1$@ mentioned or replied"; +"notification_sender_mention_reply" = "%1$@ mencionou ou respondeu"; "notification_test_push_notification_content" = "Estás a ver a notificação! Clica em mim!"; "notification_ticker_text_dm" = "%1$@: %2$@"; "notification_ticker_text_group" = "%1$@: %2$@ %3$@"; @@ -333,28 +334,26 @@ "screen_advanced_settings_element_call_base_url" = "URL base para Element Call personalizado"; "screen_advanced_settings_element_call_base_url_description" = "Define um URL base para a Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL inválido, certifica-te de que incluis o protocolo (http/https) e o endereço correto."; -"screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; -"screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; -"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; -"screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; -"screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; -"screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; -"screen_resolve_send_failure_changed_identity_title" = "Your message was not sent because %1$@’s verified identity has changed"; -"screen_resolve_send_failure_unsigned_device_primary_button_title" = "Send message anyway"; -"screen_resolve_send_failure_unsigned_device_subtitle" = "%1$@ is using one or more unverified devices. You can send the message anyway, or you can cancel for now and try again later after %2$@ has verified all their devices."; -"screen_resolve_send_failure_unsigned_device_title" = "Your message was not sent because %1$@ has not verified all devices"; -"screen_resolve_send_failure_you_unsigned_device_subtitle" = "One or more of your devices are unverified. You can send the message anyway, or you can cancel for now and try again later after you have verified all of your devices."; -"screen_resolve_send_failure_you_unsigned_device_title" = "Your message was not sent because you have not verified one or more of your devices"; +"screen_pinned_timeline_empty_state_description" = "Pressione uma mensagem e escolha \"%1$@\" para incluir aqui."; +"screen_pinned_timeline_empty_state_headline" = "Fixa mensagens importantes para que possam ser facilmente descobertas"; +"screen_reset_encryption_password_error" = "Um erro desconhecido aconteceu. Verifique se a senha da sua conta está correta e tente novamente."; +"screen_resolve_send_failure_changed_identity_primary_button_title" = "Retirar verificação e enviar"; +"screen_resolve_send_failure_changed_identity_subtitle" = "Você pode retirar sua verificação e enviar esta mensagem de qualquer maneira, ou você pode cancelar por enquanto e tentar novamente mais tarde depois de verificar novamente %1$@."; +"screen_resolve_send_failure_changed_identity_title" = "A sua mensagem não foi enviada porque a identidade verificada de %1$@ foi alterada"; +"screen_resolve_send_failure_unsigned_device_primary_button_title" = "Enviar mensagem mesmo assim"; +"screen_resolve_send_failure_unsigned_device_subtitle" = "%1$@ está usando um ou mais dispositivos não verificados. Você pode enviar a mensagem de qualquer maneira, ou você pode cancelar por enquanto e tentar novamente mais tarde depois que %2$@ tiver verificado todos os seus dispositivos."; +"screen_resolve_send_failure_unsigned_device_title" = "A sua mensagem não foi enviada porque %1$@ não verificou todos os dispositivos"; +"screen_resolve_send_failure_you_unsigned_device_subtitle" = "Um ou mais dos seus dispositivos não são verificados. Você pode enviar a mensagem de qualquer maneira, ou você pode cancelar por enquanto e tentar novamente mais tarde depois de ter verificado todos os seus dispositivos."; +"screen_resolve_send_failure_you_unsigned_device_title" = "A sua mensagem não foi enviada porque não verificou um ou mais dos seus dispositivos"; "screen_room_mentions_at_room_subtitle" = "Notificar toda a sala"; "screen_room_pinned_banner_indicator" = "%1$@ de %2$@"; "screen_room_pinned_banner_indicator_description" = "%1$@ mensagens afixadas"; -"screen_room_pinned_banner_loading_description" = "Loading message…"; +"screen_room_pinned_banner_loading_description" = "A carregar mensagem..."; "screen_room_pinned_banner_view_all_button_title" = "Ver todas"; -"screen_room_details_pinned_events_row_title" = "Pinned messages"; -"screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; -"screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; -"screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "Alterar operador de conta"; +"screen_room_details_pinned_events_row_title" = "Mensagens afixadas"; +"screen_timeline_item_menu_send_failure_changed_identity" = "Mensagem não enviada porque a identidade verificada de %1$@ foi alterada."; +"screen_timeline_item_menu_send_failure_unsigned_device" = "Mensagem não enviada porque %1$@ não verificou todos os dispositivos."; +"screen_timeline_item_menu_send_failure_you_unsigned_device" = "Mensagem não enviada porque não verificou um ou mais dos seus dispositivos."; "screen_account_provider_form_hint" = "Endereço do servidor"; "screen_account_provider_form_notice" = "Insira um termo para pesquisa ou um endereço."; "screen_account_provider_form_subtitle" = "Pesquisar por uma empresa, comunidade ou servidor privado."; @@ -432,11 +431,10 @@ "screen_chat_backup_key_backup_description" = "A cópia de segurança garante que não perdes o teu histórico de mensagens. %1$@."; "screen_chat_backup_key_backup_title" = "Cópia de segurança"; "screen_chat_backup_recovery_action_change" = "Alterar chave de recuperação"; -"screen_chat_backup_recovery_action_confirm" = "Inserir chave de recuperação"; "screen_chat_backup_recovery_action_confirm_description" = "A tua cópia de segurança das conversas está atualmente dessincronizada."; "screen_chat_backup_recovery_action_setup" = "Configurar recuperação"; "screen_chat_backup_recovery_action_setup_description" = "Obtém acesso às tuas mensagens cifradas mesmo se perderes todos os teus dispositivos ou se terminares todas as tuas sessões %1$@."; -"screen_create_account_title" = "Create account"; +"screen_create_account_title" = "Criar conta"; "screen_create_new_recovery_key_list_item_1" = "Abre a %1$@ num computador"; "screen_create_new_recovery_key_list_item_2" = "Iniciar sessão novamente"; "screen_create_new_recovery_key_list_item_3" = "Quando te for pedido para verificares o teu dispositivo, seleciona %1$@"; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Mostrar resultados só após o da sondagem"; "screen_create_poll_anonymous_headline" = "Ocultar votos"; "screen_create_poll_answer_hint" = "Opção %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "As tuas alterações não serão guardadas"; "screen_create_poll_cancel_confirmation_title_ios" = "Cancelar sondagem"; "screen_create_poll_question_desc" = "Pergunta ou tópico"; "screen_create_poll_question_hint" = "De que trata a sondagem?"; @@ -460,17 +457,17 @@ "screen_create_room_public_option_description" = "As mensagens não serão cifradas e qualquer um as poderá ler. É possível ativar a cifragem posteriormente."; "screen_create_room_public_option_title" = "Sala pública (entrada livre)"; "screen_create_room_topic_label" = "Descrição (opcional)"; -"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"; -"screen_deactivate_account_delete_all_messages_notice" = "Warning: Future users may see incomplete conversations."; -"screen_deactivate_account_description" = "Deactivating your account is %1$@, it will:"; -"screen_deactivate_account_description_bold_part" = "irreversible"; -"screen_deactivate_account_list_item_1" = "%1$@ your account (you can't log back in, and your ID can't be reused)."; -"screen_deactivate_account_list_item_1_bold_part" = "Permanently disable"; -"screen_deactivate_account_list_item_2" = "Remove you from all chat rooms."; -"screen_deactivate_account_list_item_3" = "Delete your account information from our identity server."; -"screen_deactivate_account_list_item_4" = "Your messages will still be visible to registered users but won’t be available to new or unregistered users if you choose to delete them."; -"screen_deactivate_account_title" = "Deactivate account"; +"screen_deactivate_account_confirmation_dialog_content" = "Confirme que pretende desativar a sua conta. Esta ação não pode ser desfeita."; +"screen_deactivate_account_delete_all_messages" = "Eliminar todas as minhas mensagens"; +"screen_deactivate_account_delete_all_messages_notice" = "Aviso: futuros usuários podem ver conversas incompletas."; +"screen_deactivate_account_description" = "A desativação da sua conta é %1$@, irá:"; +"screen_deactivate_account_description_bold_part" = "irreversível"; +"screen_deactivate_account_list_item_1" = "%1$@ sua conta (não pode voltar a iniciar sessão e o seu ID não pode ser reutilizado)."; +"screen_deactivate_account_list_item_1_bold_part" = "Desativar permanentemente"; +"screen_deactivate_account_list_item_2" = "Removê-lo de todas as salas de chat."; +"screen_deactivate_account_list_item_3" = "Exclua as informações da sua conta do nosso servidor de identidade."; +"screen_deactivate_account_list_item_4" = "Suas mensagens ainda estarão visíveis para usuários registrados, mas não estarão disponíveis para usuários novos ou não registrados se você optar por excluí-las."; +"screen_deactivate_account_title" = "Desativar conta"; "screen_edit_poll_delete_confirmation" = "Tens a certeza que queres apagar esta sondagem?"; "screen_edit_profile_display_name" = "Pseudónimo"; "screen_edit_profile_display_name_placeholder" = "O teu pseudónimo"; @@ -478,7 +475,7 @@ "screen_edit_profile_error_title" = "Não foi possível atualizar o perfil"; "screen_edit_profile_title" = "Editar perfil"; "screen_edit_profile_updating_details" = "A atualizar o perfil…"; -"screen_encryption_reset_action_continue_reset" = "Continue reset"; +"screen_encryption_reset_action_continue_reset" = "Continuar a reposição"; "screen_encryption_reset_bullet_1" = "Os detalhes da tua conta, contactos, preferências e lista de conversas serão mantidos."; "screen_encryption_reset_bullet_2" = "Perderás o acesso ao teu histórico de mensagens existente"; "screen_encryption_reset_bullet_3" = "Necessitarás de verificar todos os teus dispositivos e contactos novamente."; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "De grupo"; "screen_notification_settings_invite_for_me_label" = "Convites"; "screen_notification_settings_mentions_only_disclaimer" = "O teu servidor não suporta esta opção em salas cifradas, pelo que poderás não ser notificado em algumas salas."; -"screen_notification_settings_mentions_section_title" = "Menções"; "screen_notification_settings_mode_all" = "Tudo"; "screen_notification_settings_mode_mentions" = "Menções"; "screen_notification_settings_notification_section_title" = "Conversas"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Inserir..."; "screen_recovery_key_confirm_lost_recovery_key" = "Perdeste a tua chave?"; "screen_recovery_key_confirm_success" = "Chave de recuperação confirmada"; -"screen_recovery_key_confirm_title" = "Insere a tua chave de recuperação"; "screen_recovery_key_copied_to_clipboard" = "Chave de recuperação copiada"; "screen_recovery_key_generating_key" = "A gerar…"; "screen_recovery_key_save_action" = "Guardar chave"; @@ -637,11 +632,10 @@ "screen_reset_encryption_confirmation_alert_action" = "Sim, repor agora"; "screen_reset_encryption_confirmation_alert_subtitle" = "Este processo é irreversível."; "screen_reset_encryption_confirmation_alert_title" = "Tens a certeza que pretendes repor a tua cifra?"; -"screen_reset_encryption_password_placeholder" = "Inserir…"; "screen_reset_encryption_password_subtitle" = "Confirma que pretendes realmente repor a tua cifra."; "screen_reset_encryption_password_title" = "Insere a tua palavra-passe para continuares"; -"screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; -"screen_reset_identity_confirmation_title" = "Can't confirm? Go to your account to reset your identity."; +"screen_reset_identity_confirmation_subtitle" = "Está prestes a aceder à sua conta %1$@ para repor a sua identidade. Depois, você será levado de volta ao aplicativo."; +"screen_reset_identity_confirmation_title" = "Não consegue confirmar? Aceda à sua conta para repor a sua identidade."; "screen_room_alias_resolver_resolve_alias_failure" = "Não foi possível encontrar esse endereço de sala"; "screen_room_attachment_source_camera" = "Câmara"; "screen_room_attachment_source_camera_video" = "Gravar vídeo"; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Os administradores têm automaticamente privilégios de moderador"; "screen_room_change_role_moderators_title" = "Editar moderadores"; "screen_room_change_role_unsaved_changes_description" = "Tens alterações por guardar."; -"screen_room_change_role_unsaved_changes_title" = "Guardar alterações?"; "screen_room_details_add_topic_title" = "Adicionar descrição"; "screen_room_details_already_a_member" = "Já é participante"; "screen_room_details_already_invited" = "Já foi convidado"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Não foi possível dessilenciar esta sala, por favor tenta novamente."; "screen_room_details_notification_mode_custom" = "Personalizado"; "screen_room_details_notification_mode_default" = "Predefinição"; -"screen_room_details_notification_title" = "Notificações"; "screen_room_details_share_room_title" = "Partilhar sala"; "screen_room_details_title" = "Informação da sala"; "screen_room_details_updating_room" = "A atualizar sala…"; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "A banir %1$@"; "screen_room_member_list_manage_member_ban" = "Remover e banir participante"; "screen_room_member_list_manage_member_remove" = "Remover da sala"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Remover e banir"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Remover apenas"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Remover participante e proibir de se juntar no futuro?"; "screen_room_member_list_manage_member_unban_action" = "Anular banimento"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Marcar como lida"; "screen_roomlist_mark_as_unread" = "Marcar como não lida"; "screen_roomlist_room_directory_button_title" = "Consultar lista completa de salas"; -"screen_server_confirmation_change_server" = "Alterar operador de conta"; "screen_server_confirmation_message_login_element_dot_io" = "Um servidor privado para funcionários da Element."; "screen_server_confirmation_message_login_matrix_dot_org" = "A Matrix é uma rede aberta de comunicação descentralizada e segura."; "screen_server_confirmation_message_register" = "É aqui que as tuas conversas vão ficar — tal como num serviço de e-mail."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Estás prestes a terminar a tua última sessão. Se continuares, perderás o acesso às tuas mensagens cifradas."; "screen_signout_key_backup_disabled_title" = "Desativaste a cópia de segurança"; "screen_signout_key_backup_offline_subtitle" = "As tuas chaves ainda estavam a ser guardadas quando ficaste desligado. Volta a ligar-te para que as tuas chaves possam ser guardadas antes de encerrares a sessão."; -"screen_signout_key_backup_offline_title" = "As tuas chaves ainda estão a ser guardadas"; "screen_signout_key_backup_ongoing_subtitle" = "Por favor, aguarda a conclusão desta operação antes de terminares a sessão."; "screen_signout_key_backup_ongoing_title" = "As tuas chaves ainda estão a ser guardadas"; "screen_signout_recovery_disabled_subtitle" = "Estás prestes a terminar a tua última sessão. Se continuares, perderás o acesso às tuas mensagens cifradas."; "screen_signout_recovery_disabled_title" = "Recuperação não configurada"; "screen_signout_save_recovery_key_subtitle" = "Estás prestes a terminar a tua última sessão. Se continuares, poderás perder o acesso às tuas mensagens cifradas."; -"screen_signout_save_recovery_key_title" = "Guardaste a tua chave de recuperação?"; "screen_start_chat_error_starting_chat" = "Ocorreu um erro ao tentar iniciar uma conversa"; "screen_view_location_title" = "Localização"; "screen_welcome_bullet_1" = "Chamadas, sondagens, pesquisa e mais funcionalidades vão ser adicionadas ao longo do ano."; @@ -920,7 +908,6 @@ "test_language_identifier" = "pt"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Resolução de problemas"; -"troubleshoot_notifications_entry_point_title" = "Corrigir notificações"; "troubleshoot_notifications_screen_action" = "Correr testes"; "troubleshoot_notifications_screen_action_again" = "Correr testes novamente"; "troubleshoot_notifications_screen_failure" = "Alguns testes falharam. Por favor, verifica os detalhes."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Certifica que os distribuidores UnifiedPush estão disponíveis."; "troubleshoot_notifications_test_unified_push_failure" = "Nenhum distribuidor encontrado."; "troubleshoot_notifications_test_unified_push_title" = "Verificar UnifiedPush"; +"a11y_poll" = "Sondagem"; "dialog_title_error" = "Erro"; "dialog_title_success" = "Sucesso"; "notification_fallback_content" = "Notificação"; "notification_invitation_action_join" = "Entrar"; +"notification_invitation_action_reject" = "Rejeitar"; "notification_room_action_mark_as_read" = "Marcar como lida"; "notification_room_action_quick_reply" = "Resposta rápida"; +"screen_pinned_timeline_screen_title_empty" = "Mensagens afixadas"; "screen_room_mentions_at_room_title" = "Toda a gente"; +"screen_account_provider_change" = "Alterar operador de conta"; "screen_account_provider_signin_subtitle" = "É aqui que as tuas conversas vão ficar — tal como num serviço de e-mail."; "screen_account_provider_signup_subtitle" = "É aqui que as tuas conversas vão ficar — tal como num serviço de e-mail."; "screen_analytics_settings_help_us_improve" = "Partilhe dados de utilização anónimos para nos ajudar a identificar problemas."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Poderás voltar a ver todas as suas mensagens."; "screen_blocked_users_unblock_alert_title" = "Desbloquear utilizador"; "screen_bug_report_rash_logs_alert_title" = "A %1$@ teve uma falha da última vez que foi utilizada. Gostarias de partilhar um relatório de acidente connosco?"; +"screen_chat_backup_recovery_action_confirm" = "Insere a chave de recuperação"; +"screen_create_poll_cancel_confirmation_content_ios" = "As tuas alterações não serão guardadas"; "screen_create_room_add_people_title" = "Convidar pessoas"; "screen_create_room_room_name_label" = "Nome da sala"; "screen_create_room_title" = "Criar uma sala"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Editar sondagem"; "screen_identity_use_another_device" = "Utilizar outro dispositivo"; "screen_login_subtitle" = "A Matrix é uma rede aberta de comunicação descentralizada e segura."; +"screen_notification_settings_mentions_section_title" = "Menções"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Tentar novamente"; +"screen_recovery_key_confirm_title" = "Insere a tua chave de recuperação"; "screen_report_content_block_user" = "Bloquear utilizador"; +"screen_reset_encryption_password_placeholder" = "Inserir..."; "screen_room_attachment_source_camera_photo" = "Tirar foto"; "screen_room_change_permissions_everyone" = "Toda a gente"; "screen_room_change_permissions_member_moderation" = "Moderação de participantes"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Administradores"; "screen_room_change_role_section_moderators" = "Moderadores"; "screen_room_change_role_section_users" = "Participantes"; +"screen_room_change_role_unsaved_changes_title" = "Guardar alterações?"; "screen_room_details_invite_people_title" = "Convidar pessoas"; "screen_room_details_leave_conversation_title" = "Sair da conversa"; "screen_room_details_leave_room_title" = "Sair da sala"; +"screen_room_details_notification_title" = "Notificações"; "screen_room_details_roles_and_permissions" = "Cargos e permissões"; "screen_room_details_room_name_label" = "Nome da sala"; "screen_room_details_security_title" = "Segurança"; "screen_room_details_topic_title" = "Descrição"; "screen_room_error_failed_processing_media" = "Falha ao processar multimédia para carregamento, por favor tente novamente."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Remover e banir participante"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Menções ou palavras-chave"; "screen_roomlist_filter_people" = "Pessoas"; +"screen_server_confirmation_change_server" = "Alterar operador de conta"; "screen_signout_confirmation_dialog_submit" = "Terminar sessão"; "screen_signout_confirmation_dialog_title" = "Terminar sessão"; +"screen_signout_key_backup_offline_title" = "As tuas chaves ainda estão a ser guardadas"; "screen_signout_preference_item" = "Terminar sessão"; +"screen_signout_save_recovery_key_title" = "Guardaste a tua chave de recuperação?"; +"troubleshoot_notifications_entry_point_title" = "Corrigir notificações"; diff --git a/ElementX/Resources/Localizations/pt.lproj/Localizable.stringsdict b/ElementX/Resources/Localizations/pt.lproj/Localizable.stringsdict index be8835fae3..eb8934967a 100644 --- a/ElementX/Resources/Localizations/pt.lproj/Localizable.stringsdict +++ b/ElementX/Resources/Localizations/pt.lproj/Localizable.stringsdict @@ -205,9 +205,9 @@ NSStringFormatValueTypeKey d one - %1$d Pinned message + %1$d Mensagem afixada other - %1$d Pinned messages + %1$d Mensagens afixadas screen_room_member_list_header_title diff --git a/ElementX/Resources/Localizations/ro.lproj/Localizable.strings b/ElementX/Resources/Localizations/ro.lproj/Localizable.strings index 6ab8b1f96c..93ee5c9832 100644 --- a/ElementX/Resources/Localizations/ro.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ro.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Pauză"; "a11y_pin_field" = "Câmp PIN"; "a11y_play" = "Redați"; -"a11y_poll" = "Sondaj"; "a11y_poll_end" = "Sondaj încheiat"; "a11y_react_with" = "Reacționați cu %1$@"; "a11y_react_with_other_emojis" = "Reacționați cu alte emoji-uri"; @@ -41,6 +40,7 @@ "action_create" = "Creați"; "action_create_a_room" = "Creați o cameră"; "action_deactivate" = "Deactivate"; +"action_deactivate_account" = "Deactivate account"; "action_decline" = "Refuzați"; "action_delete_poll" = "Ștergeți sondajul"; "action_disable" = "Dezactivați"; @@ -64,6 +64,7 @@ "action_leave" = "Părăsiți"; "action_leave_conversation" = "Părăsiți conversația"; "action_leave_room" = "Părăsiți camera"; +"action_load_more" = "Încărcați mai mult"; "action_manage_account" = "Administrare cont"; "action_manage_devices" = "Gestionare dispozitive"; "action_message" = "Mesaj"; @@ -93,6 +94,7 @@ "action_send_message" = "Trimiteți mesajul"; "action_share" = "Partajați"; "action_share_link" = "Partajați linkul"; +"action_show" = "Show"; "action_sign_in_again" = "Autentificați-vă din nou"; "action_signout" = "Deconectați-vă"; "action_signout_anyway" = "Deconectați-vă oricum"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "View in timeline"; "action_view_source" = "Vedeți sursă"; "action_yes" = "Da"; -"action.load_more" = "Încărcați mai mult"; -"action_deactivate_account" = "Deactivate account"; "banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; @@ -162,6 +162,7 @@ "common_modern" = "Modern"; "common_mute" = "Dezactivați sunetul"; "common_no_results" = "Niciun rezultat"; +"common_no_room_name" = "Fără nume de cameră"; "common_offline" = "Deconectat"; "common_optic_id_ios" = "Optic ID"; "common_or" = "sau"; @@ -170,6 +171,8 @@ "common_permalink" = "Permalink"; "common_permission" = "Permisiune"; "common_please_wait" = "Va rugam asteptati…"; +"common_poll_end_confirmation" = "Sunteți sigur că doriți să încheiați acest sondaj?"; +"common_poll_summary" = "Sondajul %1$@"; "common_poll_total_votes" = "Total voturi: %1$@"; "common_poll_undisclosed_text" = "Rezultatele vor fi afișate după încheierea sondajului"; "common_privacy_policy" = "Politica de confidențialitate"; @@ -200,6 +203,7 @@ "common_settings" = "Setări"; "common_shared_location" = "Locație partajată"; "common_signing_out" = "Deconectare în curs"; +"common_something_went_wrong" = "Ceva nu a mers bine"; "common_starting_chat" = "Se începe conversația…"; "common_sticker" = "Autocolant"; "common_success" = "Succes"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "Despre ce este vorba în această cameră?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Nu s-a putut decripta"; +"common_unable_to_decrypt_no_access" = "Nu aveți acces la acest mesaj"; "common_unable_to_invite_message" = "Nu am putut trimite invitații unuia sau mai multor utilizatori."; "common_unable_to_invite_title" = "Nu s-a putut trimite invitația (invitațiile)"; "common_unlock" = "Deblocare"; @@ -221,24 +226,21 @@ "common_username" = "Utilizator"; "common_verification_cancelled" = "Verificare anulată"; "common_verification_complete" = "Verificare completă"; +"common_verify_device" = "Verificați dispozitivul"; "common_video" = "Video"; "common_voice_message" = "Mesaj vocal"; "common_waiting" = "Se aşteaptă…"; "common_waiting_for_decryption_key" = "Mesaj în așteptare"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "Do not show this again"; "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "Trimiteți către"; "common.you" = "You"; -"common_no_room_name" = "Fără nume de cameră"; -"common_poll_end_confirmation" = "Sunteți sigur că doriți să încheiați acest sondaj?"; -"common_poll_summary" = "Sondajul %1$@"; -"common_something_went_wrong" = "Ceva nu a mers bine"; -"common_unable_to_decrypt_no_access" = "Nu aveți acces la acest mesaj"; -"common_verify_device" = "Verificați dispozitivul"; "confirm_recovery_key_banner_message" = "Backup-ul pentru chat nu este sincronizat în prezent. Trebuie să confirmați cheia de recuperare pentru a menține accesul la backup."; "confirm_recovery_key_banner_title" = "Confirmați cheia de recuperare"; "crash_detection_dialog_content" = "%1$@ s-a blocat ultima dată când a fost folosit. Doriți să ne trimiteți un raport?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "Pentru a permite aplicației să utilizeze camera, vă rugăm să acordați permisiunea în setările sistemului."; "dialog_permission_generic" = "Vă rugăm să acordați permisiunea în setările sistemului."; "dialog_permission_location_description_ios" = "Acordați accesul în Setări -> Locație."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "Notificări silențioase"; "notification_incoming_call" = "Apel primit"; "notification_inline_reply_failed" = "** Trimiterea eșuată - vă rugăm să deschideți camera"; -"notification_invitation_action_reject" = "Respingeți"; "notification_invite_body" = "V-a invitat la o discuție"; "notification_invite_body_with_sender" = "%1$@ invited you to chat"; "notification_mentioned_you_body" = "%1$@ v-a menționat"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "URL invalid, vă rugăm să vă asigurați că includeți protocolul (http/https) și adresa corectă."; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; -"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; "screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "Schimbați furnizorul contului"; "screen_account_provider_form_hint" = "Adresa Homeserver-ului"; "screen_account_provider_form_notice" = "Introduceţi un termen de căutare sau o adresă de domeniu."; "screen_account_provider_form_subtitle" = "Căutați o companie, o comunitate sau un server privat."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "Backup vă asigură că nu pierdeți istoricul mesajelor. %1$@."; "screen_chat_backup_key_backup_title" = "Backup"; "screen_chat_backup_recovery_action_change" = "Schimbați cheia de recuperare"; -"screen_chat_backup_recovery_action_confirm" = "Confirmați cheia de recuperare"; "screen_chat_backup_recovery_action_confirm_description" = "Backup-ul pentru chat nu este sincronizat în prezent."; "screen_chat_backup_recovery_action_setup" = "Configurați recuperarea"; "screen_chat_backup_recovery_action_setup_description" = "Obțineți acces la mesajele dumneavoastră criptate dacă vă pierdeți toate dispozitivele sau sunteți deconectat de la %1$@ peste tot."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Afișați rezultatele numai după încheierea sondajului"; "screen_create_poll_anonymous_headline" = "Sondaj anonim"; "screen_create_poll_answer_hint" = "Opțiune %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Modificările dumneavoastră nu vor fi salvate"; "screen_create_poll_cancel_confirmation_title_ios" = "Renunțați la sondaj"; "screen_create_poll_question_desc" = "Întrebare sau subiect"; "screen_create_poll_question_hint" = "Despre ce este sondajul?"; @@ -480,7 +477,7 @@ "screen_edit_profile_updating_details" = "Se actualizează profilul..."; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history"; +"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Discuții de grup"; "screen_notification_settings_invite_for_me_label" = "Invitații"; "screen_notification_settings_mentions_only_disclaimer" = "Serverul dumneavoastră nu acceptă această opțiune în camerele criptate, este posibil să nu primiți notificări în unele camere."; -"screen_notification_settings_mentions_section_title" = "Mențiuni"; "screen_notification_settings_mode_all" = "Toate"; "screen_notification_settings_mode_mentions" = "Mențiuni"; "screen_notification_settings_notification_section_title" = "Anunță-mă pentru"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Introduceți..."; "screen_recovery_key_confirm_lost_recovery_key" = "Ați pierdut cheia de recuperare?"; "screen_recovery_key_confirm_success" = "Cheia de recuperare confirmată"; -"screen_recovery_key_confirm_title" = "Confirmați cheia de recuperare"; "screen_recovery_key_copied_to_clipboard" = "Cheia de recuperare copiată"; "screen_recovery_key_generating_key" = "Se generează..."; "screen_recovery_key_save_action" = "Salvați cheia de recuperare"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Yes, reset now"; "screen_reset_encryption_confirmation_alert_subtitle" = "This process is irreversible."; "screen_reset_encryption_confirmation_alert_title" = "Are you sure you want to reset your identity?"; -"screen_reset_encryption_password_placeholder" = "Enter…"; "screen_reset_encryption_password_subtitle" = "Confirm that you want to reset your identity."; "screen_reset_encryption_password_title" = "Enter your account password to continue"; "screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Administratorii au automat privilegii de moderator"; "screen_room_change_role_moderators_title" = "Editați moderatorii"; "screen_room_change_role_unsaved_changes_description" = "Aveți modificări nesalvate."; -"screen_room_change_role_unsaved_changes_title" = "Salvați modificările?"; "screen_room_details_add_topic_title" = "Adăugare subiect"; "screen_room_details_already_a_member" = "Deja membru"; "screen_room_details_already_invited" = "Deja invitat"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Activarea notificarilor pentru această cameră a eșuat, încercați din nou."; "screen_room_details_notification_mode_custom" = "Personalizat"; "screen_room_details_notification_mode_default" = "Implicit"; -"screen_room_details_notification_title" = "Notificări"; "screen_room_details_share_room_title" = "Partajați camera"; "screen_room_details_title" = "Informatii camera"; "screen_room_details_updating_room" = "Se actualizează camera…"; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Se interzice %1$@"; "screen_room_member_list_manage_member_ban" = "Eliminați și interziceți membrul"; "screen_room_member_list_manage_member_remove" = "Înlăturați membrul"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Înlăturați și interziceți membrul"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Doar înlăturare"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Înlăturați membrul și interziceți-i să se alăture în viitor?"; "screen_room_member_list_manage_member_unban_action" = "Anulare excludere"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Marcați ca citită"; "screen_roomlist_mark_as_unread" = "Marcați ca necitită"; "screen_roomlist_room_directory_button_title" = "Răsfoiți toate camerele"; -"screen_server_confirmation_change_server" = "Schimbați furnizorul contului"; "screen_server_confirmation_message_login_element_dot_io" = "Un server privat pentru angajații Element."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix este o rețea deschisă pentru o comunicare sigură și descentralizată."; "screen_server_confirmation_message_register" = "Aici vor trăi conversațiile dvs. - la fel cum ați folosi un furnizor de e-mail pentru a vă păstra e-mailurile."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Sunteți pe cale să vă deconectați de la ultima sesiune. Dacă vă deconectați acum, veți pierde accesul la mesajele criptate."; "screen_signout_key_backup_disabled_title" = "Ați dezactivat backup-ul"; "screen_signout_key_backup_offline_subtitle" = "Cheile dumneavoastră erau încă în curs de backup atunci când ați fost deconectat. Reconectați-vă pentru ca cheile dumneavoastră să poată fi salvate înainte de a vă deconecta."; -"screen_signout_key_backup_offline_title" = "Cheile dumneavoastră sunt încă în curs de backup"; "screen_signout_key_backup_ongoing_subtitle" = "Vă rugăm să așteptați până la finalizarea acestui proces înainte de a vă deconecta."; "screen_signout_key_backup_ongoing_title" = "Cheile dumneavoastră sunt încă în curs de backup"; "screen_signout_recovery_disabled_subtitle" = "Sunteți pe cale să vă deconectați de la ultima sesiune. Dacă vă deconectați acum, veți pierde accesul la mesajele criptate."; "screen_signout_recovery_disabled_title" = "Recuperarea nu este configurată"; "screen_signout_save_recovery_key_subtitle" = "Sunteți pe cale să vă deconectați de la ultima sesiune. Dacă vă deconectați acum, este posibil să pierdeți accesul la mesajele criptate."; -"screen_signout_save_recovery_key_title" = "Ați salvat cheia de recuperare?"; "screen_start_chat_error_starting_chat" = "A apărut o eroare la încercarea începerii conversației"; "screen_view_location_title" = "Locație"; "screen_welcome_bullet_1" = "Apelurile, sondajele, căutare și multe altele vor fi adăugate în cursul acestui an."; @@ -920,7 +908,6 @@ "test_language_identifier" = "ro"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Depanare"; -"troubleshoot_notifications_entry_point_title" = "Depanați notificările"; "troubleshoot_notifications_screen_action" = "Rulați testele"; "troubleshoot_notifications_screen_action_again" = "Rulați din nou testele"; "troubleshoot_notifications_screen_failure" = "Unele teste au eșuat. Vă rugăm să verificați detaliile."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Asigurați-vă că distribuitorii UnifiedPush sunt disponibili."; "troubleshoot_notifications_test_unified_push_failure" = "Nu au fost găsiți distribuitori push."; "troubleshoot_notifications_test_unified_push_title" = "Verificați UnifiedPush"; +"a11y_poll" = "Sondaj"; "dialog_title_error" = "Eroare"; "dialog_title_success" = "Succes"; "notification_fallback_content" = "Notificare"; "notification_invitation_action_join" = "Alăturați-vă"; +"notification_invitation_action_reject" = "Respinge"; "notification_room_action_mark_as_read" = "Marcați ca citită"; "notification_room_action_quick_reply" = "Raspuns rapid"; +"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_room_mentions_at_room_title" = "Toți"; +"screen_account_provider_change" = "Schimbați furnizorul contului"; "screen_account_provider_signin_subtitle" = "Aici vor trăi conversațiile dvs. - la fel cum ați folosi un furnizor de e-mail pentru a vă păstra e-mailurile."; "screen_account_provider_signup_subtitle" = "Aici vor trăi conversațiile dvs. - la fel cum ați folosi un furnizor de e-mail pentru a vă păstra e-mailurile."; "screen_analytics_settings_help_us_improve" = "Distribuiți date anonime de utilizare pentru a ne ajuta să identificăm probleme."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "La deblocarea utilizatorului, veți putea vedea din nou toate mesajele de la acesta."; "screen_blocked_users_unblock_alert_title" = "Deblocați utilizatorul"; "screen_bug_report_rash_logs_alert_title" = "%1$@ s-a blocat ultima dată când a fost folosit. Doriți să ne trimiteți un raport?"; +"screen_chat_backup_recovery_action_confirm" = "Introduceți cheia de recuperare"; +"screen_create_poll_cancel_confirmation_content_ios" = "Modificările dumneavoastră nu vor fi salvate"; "screen_create_room_add_people_title" = "Invitați prieteni"; "screen_create_room_room_name_label" = "Numele camerei"; "screen_create_room_title" = "Creați o cameră"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Editați sondajul"; "screen_identity_use_another_device" = "Utilizați un alt dispozitiv"; "screen_login_subtitle" = "Matrix este o rețea deschisă pentru o comunicare sigură și descentralizată."; +"screen_notification_settings_mentions_section_title" = "Mențiuni"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Încercați din nou"; +"screen_recovery_key_confirm_title" = "Confirmați cheia de recuperare"; "screen_report_content_block_user" = "Blocați utilizatorul"; +"screen_reset_encryption_password_placeholder" = "Introduceți..."; "screen_room_attachment_source_camera_photo" = "Faceți o fotografie"; "screen_room_change_permissions_everyone" = "Toți"; "screen_room_change_permissions_member_moderation" = "Moderarea membrilor"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Administratori"; "screen_room_change_role_section_moderators" = "Moderatori"; "screen_room_change_role_section_users" = "Membri"; +"screen_room_change_role_unsaved_changes_title" = "Salvați modificările?"; "screen_room_details_invite_people_title" = "Invitați prieteni"; "screen_room_details_leave_conversation_title" = "Părăsiți conversația"; "screen_room_details_leave_room_title" = "Părăsiți camera"; +"screen_room_details_notification_title" = "Notificări"; "screen_room_details_roles_and_permissions" = "Roluri și permisiuni"; "screen_room_details_room_name_label" = "Numele camerei"; "screen_room_details_security_title" = "Securitate"; "screen_room_details_topic_title" = "Subiect"; "screen_room_error_failed_processing_media" = "Procesarea datelor media a eșuat, vă rugăm să încercați din nou."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Eliminați și interziceți membrul"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Numai mențiuni și cuvinte cheie"; "screen_roomlist_filter_people" = "Persoane"; +"screen_server_confirmation_change_server" = "Schimbați furnizorul contului"; "screen_signout_confirmation_dialog_submit" = "Deconectați-vă"; "screen_signout_confirmation_dialog_title" = "Deconectați-vă"; +"screen_signout_key_backup_offline_title" = "Cheile dumneavoastră sunt încă în curs de backup"; "screen_signout_preference_item" = "Deconectați-vă"; +"screen_signout_save_recovery_key_title" = "Ați salvat cheia de recuperare?"; +"troubleshoot_notifications_entry_point_title" = "Depanați notificările"; diff --git a/ElementX/Resources/Localizations/ru.lproj/Localizable.strings b/ElementX/Resources/Localizations/ru.lproj/Localizable.strings index eed53c51b3..6841ffb00d 100644 --- a/ElementX/Resources/Localizations/ru.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ru.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Приостановить"; "a11y_pin_field" = "Поле PIN-кода"; "a11y_play" = "Воспроизвести"; -"a11y_poll" = "Опрос"; "a11y_poll_end" = "Опрос завершен"; "a11y_react_with" = "Реагировать вместе с %1$@"; "a11y_react_with_other_emojis" = "Реакция с помощью эмодзи"; @@ -41,6 +40,7 @@ "action_create" = "Создать"; "action_create_a_room" = "Создать комнату"; "action_deactivate" = "Деактивировать"; +"action_deactivate_account" = "Отключить учётную запись"; "action_decline" = "Отклонить"; "action_delete_poll" = "Удалить опрос"; "action_disable" = "Отключить"; @@ -64,6 +64,7 @@ "action_leave" = "Выйти"; "action_leave_conversation" = "Покинуть беседу"; "action_leave_room" = "Покинуть комнату"; +"action_load_more" = "Загрузить еще"; "action_manage_account" = "Настройки аккаунта"; "action_manage_devices" = "Управление устройствами"; "action_message" = "Сообщение"; @@ -93,6 +94,7 @@ "action_send_message" = "Отправить сообщение"; "action_share" = "Поделиться"; "action_share_link" = "Поделиться ссылкой"; +"action_show" = "Show"; "action_sign_in_again" = "Повторите вход"; "action_signout" = "Выйти"; "action_signout_anyway" = "Все равно выйти"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "Просмотр в хронологии"; "action_view_source" = "Показать источник"; "action_yes" = "Да"; -"action.load_more" = "Загрузить еще"; -"action_deactivate_account" = "Отключить учётную запись"; "banner_migrate_to_native_sliding_sync_action" = "Выйти и обновить"; "banner_migrate_to_native_sliding_sync_description" = "Теперь ваш сервер поддерживает новый, более быстрый протокол. Выйдите из системы и снова войдите в систему для обновления прямо сейчас. Сделав это сейчас, вы сможете избежать принудительного выхода из системы при последующем удалении старого протокола."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Ваш homeserver больше не поддерживает старый протокол. Пожалуйста, выйдите из системы и войдите снова, чтобы продолжить использование приложения."; @@ -162,6 +162,7 @@ "common_modern" = "Современный"; "common_mute" = "Без звука"; "common_no_results" = "Ничего не найдено"; +"common_no_room_name" = "Нету названия комнаты"; "common_offline" = "Не в сети"; "common_optic_id_ios" = "Оптический идентификатор"; "common_or" = "или"; @@ -170,6 +171,8 @@ "common_permalink" = "Постоянная ссылка"; "common_permission" = "Разрешение"; "common_please_wait" = "Подождите..."; +"common_poll_end_confirmation" = "Вы действительно хотите завершить данный опрос?"; +"common_poll_summary" = "Опрос: %1$@"; "common_poll_total_votes" = "Всего голосов: %1$@"; "common_poll_undisclosed_text" = "Результаты будут показаны после завершения опроса"; "common_privacy_policy" = "Политика конфиденциальности"; @@ -200,6 +203,7 @@ "common_settings" = "Настройки"; "common_shared_location" = "Делится местонахождением"; "common_signing_out" = "Выход…"; +"common_something_went_wrong" = "Что-то пошло не так"; "common_starting_chat" = "Начало чата…"; "common_sticker" = "Стикер"; "common_success" = "Успешно"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "О чем эта комната?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Невозможно расшифровать"; +"common_unable_to_decrypt_no_access" = "Вы не имеете доступа к этому сообщению"; "common_unable_to_invite_message" = "Не удалось отправить приглашения одному или нескольким пользователям."; "common_unable_to_invite_title" = "Не удалось отправить приглашение(я)"; "common_unlock" = "Разблокировать"; @@ -221,24 +226,21 @@ "common_username" = "Имя пользователя"; "common_verification_cancelled" = "Проверка отменена"; "common_verification_complete" = "Проверка завершена"; +"common_verify_device" = "Подтверждение устройства"; "common_video" = "Видео"; "common_voice_message" = "Голосовое сообщение"; "common_waiting" = "Ожидание…"; "common_waiting_for_decryption_key" = "Ожидание ключа расшифровки"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "Не показывать больше"; "common.open_source_licenses" = "Лицензии с открытым исходным кодом"; "common.pinned" = "Закрепленный"; "common.send_to" = "Отправить"; "common.you" = "You"; -"common_no_room_name" = "Нету названия комнаты"; -"common_poll_end_confirmation" = "Вы действительно хотите завершить данный опрос?"; -"common_poll_summary" = "Опрос: %1$@"; -"common_something_went_wrong" = "Что-то пошло не так"; -"common_unable_to_decrypt_no_access" = "Вы не имеете доступа к этому сообщению"; -"common_verify_device" = "Подтверждение устройства"; "confirm_recovery_key_banner_message" = "В настоящее время резервная копия вашего чата не синхронизирована. Требуется подтвердить вашим ключом восстановления, чтобы сохранить доступ к резервной копии чата."; "confirm_recovery_key_banner_title" = "Введите ключ восстановления"; "crash_detection_dialog_content" = "При последнем использовании %1$@ произошел сбой. Хотите поделиться отчетом о сбое?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "Чтобы приложение могло использовать камеру, предоставьте разрешение в системных настройках."; "dialog_permission_generic" = "Пожалуйста, предоставьте разрешение в системных настройках."; "dialog_permission_location_description_ios" = "Предоставьте доступ в меню «Настройки» -> «Местоположение»."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "Бесшумные уведомления"; "notification_incoming_call" = "Входящий вызов"; "notification_inline_reply_failed" = "** Не удалось отправить - пожалуйста, откройте комнату"; -"notification_invitation_action_reject" = "Отклонить"; "notification_invite_body" = "Пригласил вас в чат"; "notification_invite_body_with_sender" = "%1$@ пригласил вас в чат"; "notification_mentioned_you_body" = "Упомянул вас: %1$@"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "Адрес указан неверно, удостоверьтесь, что вы указали протокол (http/https) и правильный адрес."; "screen_pinned_timeline_empty_state_description" = "Нажмите на сообщение и выберите “%1$@”, чтобы добавить его сюда."; "screen_pinned_timeline_empty_state_headline" = "Закрепите важные сообщения, чтобы их можно было легко найти"; -"screen_pinned_timeline_screen_title_empty" = "Закрепленные сообщения"; "screen_reset_encryption_password_error" = "Произошла неизвестная ошибка. Проверьте правильность пароля учетной записи и повторите попытку."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Отозвать верификацию и отправить"; "screen_resolve_send_failure_changed_identity_subtitle" = "Вы можете отозвать свою верификацию и отправить это сообщение в любом случае или вы можете отменить ее сейчас и повторить попытку позже после повторной верификации %1$@."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Сообщение не отправлено, потому что верифицированная личность %1$@ изменилась."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Сообщение не отправлено, потому что %1$@ не проверил одно или несколько устройств."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Сообщение не отправлено, поскольку вы не подтвердили одно или несколько своих устройств."; -"screen_account_provider_change" = "Переключить аккаунт"; "screen_account_provider_form_hint" = "Адрес домашнего сервера"; "screen_account_provider_form_notice" = "Введите поисковый запрос или адрес домена."; "screen_account_provider_form_subtitle" = "Поиск компании, сообщества или частного сервера."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "Резервное копирование гарантирует, что вы не потеряете историю сообщений. %1$@."; "screen_chat_backup_key_backup_title" = "Резервное копирование"; "screen_chat_backup_recovery_action_change" = "Изменить ключ восстановления"; -"screen_chat_backup_recovery_action_confirm" = "Ввести ключ восстановления"; "screen_chat_backup_recovery_action_confirm_description" = "Резервная копия чата в настоящее время не синхронизирована."; "screen_chat_backup_recovery_action_setup" = "Настроить восстановление"; "screen_chat_backup_recovery_action_setup_description" = "Получите доступ к зашифрованным сообщениям, если вы потеряете все свои устройства или выйдете из системы %1$@ отовсюду."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Показывать результаты только после окончания опроса"; "screen_create_poll_anonymous_headline" = "Анонимный опрос"; "screen_create_poll_answer_hint" = "Настройка %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Вы действительно хотите отменить этот опрос"; "screen_create_poll_cancel_confirmation_title_ios" = "Отменить опрос"; "screen_create_poll_question_desc" = "Вопрос или тема"; "screen_create_poll_question_hint" = "Тема опроса?"; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Групповые чаты"; "screen_notification_settings_invite_for_me_label" = "Приглашения"; "screen_notification_settings_mentions_only_disclaimer" = "Ваш домашний сервер не поддерживает эту опцию в зашифрованных комнатах, в некоторых комнатах вы можете не получать уведомления."; -"screen_notification_settings_mentions_section_title" = "Упоминания"; "screen_notification_settings_mode_all" = "Все"; "screen_notification_settings_mode_mentions" = "Упоминания"; "screen_notification_settings_notification_section_title" = "Уведомить меня"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Вход…"; "screen_recovery_key_confirm_lost_recovery_key" = "Потеряли ключ восстановления?"; "screen_recovery_key_confirm_success" = "Ключ восстановления подтвержден"; -"screen_recovery_key_confirm_title" = "Подтвердите ключ восстановления"; "screen_recovery_key_copied_to_clipboard" = "Ключ восстановления скопирован"; "screen_recovery_key_generating_key" = "Генерация…"; "screen_recovery_key_save_action" = "Сохранить ключ восстановления"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Да, сбросить сейчас"; "screen_reset_encryption_confirmation_alert_subtitle" = "Этот процесс необратим."; "screen_reset_encryption_confirmation_alert_title" = "Вы действительно хотите сбросить шифрование?"; -"screen_reset_encryption_password_placeholder" = "Ввод..."; "screen_reset_encryption_password_subtitle" = "Подтвердите, что вы хотите сбросить шифрование."; "screen_reset_encryption_password_title" = "Введите пароль своей учетной записи, чтобы продолжить"; "screen_reset_identity_confirmation_subtitle" = "Вы собираетесь перейти в свою учетную запись %1$@, чтобы сбросить идентификацию. После этого вы вернетесь в приложение."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Администраторы автоматически получают права модератора"; "screen_room_change_role_moderators_title" = "Редактировать роль модераторов"; "screen_room_change_role_unsaved_changes_description" = "У вас есть несохраненные изменения."; -"screen_room_change_role_unsaved_changes_title" = "Сохранить изменения?"; "screen_room_details_add_topic_title" = "Добавить тему"; "screen_room_details_already_a_member" = "Уже зарегистрирован"; "screen_room_details_already_invited" = "Уже приглашены"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Не удалось включить звук в эту комнату, попробуйте еще раз."; "screen_room_details_notification_mode_custom" = "Пользовательский"; "screen_room_details_notification_mode_default" = "По умолчанию"; -"screen_room_details_notification_title" = "Уведомления"; "screen_room_details_share_room_title" = "Поделиться комнатой"; "screen_room_details_title" = "Информация о комнате"; "screen_room_details_updating_room" = "Обновление комнаты…"; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Блокировка %1$@"; "screen_room_member_list_manage_member_ban" = "Удалить и заблокировать участника"; "screen_room_member_list_manage_member_remove" = "Удалить участника из комнаты"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Удалить и запретить участника"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Только удалить участника"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Удалить участника и запретить присоединяться в будущем?"; "screen_room_member_list_manage_member_unban_action" = "Разблокировать"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Пометить как прочитанное"; "screen_roomlist_mark_as_unread" = "Пометить как непрочитанное"; "screen_roomlist_room_directory_button_title" = "Просмотреть все комнаты"; -"screen_server_confirmation_change_server" = "Сменить учетную запись"; "screen_server_confirmation_message_login_element_dot_io" = "Частный сервер для сотрудников Element."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix — это открытая сеть для безопасной децентрализованной связи."; "screen_server_confirmation_message_register" = "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Вы собираетесь выйти из последнего сеанса. Если вы выйдете из системы сейчас, вы потеряете доступ к зашифрованным сообщениям."; "screen_signout_key_backup_disabled_title" = "Вы отключили резервное копирование"; "screen_signout_key_backup_offline_subtitle" = "Когда вы перешли в автономный режим, резервное копирование ваших ключей продолжалось. Повторно подключитесь, чтобы перед выходом из системы можно было создать резервную копию ключей."; -"screen_signout_key_backup_offline_title" = "Резервное копирование ключей все еще продолжается"; "screen_signout_key_backup_ongoing_subtitle" = "Пожалуйста, дождитесь завершения процесса, прежде чем выходить из системы."; "screen_signout_key_backup_ongoing_title" = "Резервное копирование ключей все еще продолжается"; "screen_signout_recovery_disabled_subtitle" = "Вы собираетесь выйти из последнего сеанса. Если вы выйдете из системы сейчас, вы потеряете доступ к зашифрованным сообщениям."; "screen_signout_recovery_disabled_title" = "Восстановление не настроено"; "screen_signout_save_recovery_key_subtitle" = "Вы собираетесь выйти из последнего сеанса. Если вы выйдете из системы сейчас, вы можете потерять доступ к зашифрованным сообщениям."; -"screen_signout_save_recovery_key_title" = "Вы сохранили свой ключ восстановления?"; "screen_start_chat_error_starting_chat" = "Произошла ошибка при попытке открытия комнаты"; "screen_view_location_title" = "Местоположение"; "screen_welcome_bullet_1" = "Звонки, опросы, поиск и многое другое будут добавлены позже в этом году."; @@ -920,7 +908,6 @@ "test_language_identifier" = "ru"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Устранение неполадок"; -"troubleshoot_notifications_entry_point_title" = "Уведомления об устранении неполадок"; "troubleshoot_notifications_screen_action" = "Выполнение тестов"; "troubleshoot_notifications_screen_action_again" = "Повторное выполнение тестов"; "troubleshoot_notifications_screen_failure" = "Некоторые тесты провалились. Пожалуйста, проверьте детали."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Убедитесь, что дистрибьюторы UnifiedPush доступны."; "troubleshoot_notifications_test_unified_push_failure" = "Поставщиков push-уведомлений не найдено."; "troubleshoot_notifications_test_unified_push_title" = "Проверка UnifiedPush"; +"a11y_poll" = "Опрос"; "dialog_title_error" = "Ошибка"; "dialog_title_success" = "Успешно"; "notification_fallback_content" = "Уведомление"; "notification_invitation_action_join" = "Присоединиться"; +"notification_invitation_action_reject" = "Отклонить"; "notification_room_action_mark_as_read" = "Пометить как прочитанное"; "notification_room_action_quick_reply" = "Быстрый ответ"; +"screen_pinned_timeline_screen_title_empty" = "Закрепленные сообщения"; "screen_room_mentions_at_room_title" = "Для всех"; +"screen_account_provider_change" = "Сменить поставщика учетной записи"; "screen_account_provider_signin_subtitle" = "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем."; "screen_account_provider_signup_subtitle" = "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем."; "screen_analytics_settings_help_us_improve" = "Предоставлять анонимные данные об использовании, чтобы помочь нам выявить проблемы."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Вы снова сможете увидеть все сообщения."; "screen_blocked_users_unblock_alert_title" = "Разблокировать пользователя"; "screen_bug_report_rash_logs_alert_title" = "При последнем использовании %1$@ произошел сбой. Хотите поделиться отчетом о сбое?"; +"screen_chat_backup_recovery_action_confirm" = "Введите ключ восстановления"; +"screen_create_poll_cancel_confirmation_content_ios" = "Ваши изменения не будут сохранены"; "screen_create_room_add_people_title" = "Пригласить в комнату"; "screen_create_room_room_name_label" = "Название комнаты"; "screen_create_room_title" = "Создать комнату"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Редактировать опрос"; "screen_identity_use_another_device" = "Используйте другое устройство"; "screen_login_subtitle" = "Matrix — это открытая сеть для безопасной децентрализованной связи."; +"screen_notification_settings_mentions_section_title" = "Упоминания"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Повторить попытку"; +"screen_recovery_key_confirm_title" = "Введите ключ восстановления"; "screen_report_content_block_user" = "Заблокировать пользователя"; +"screen_reset_encryption_password_placeholder" = "Вход…"; "screen_room_attachment_source_camera_photo" = "Сделать фото"; "screen_room_change_permissions_everyone" = "Для всех"; "screen_room_change_permissions_member_moderation" = "Модерация участников"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Администраторы"; "screen_room_change_role_section_moderators" = "Модераторы"; "screen_room_change_role_section_users" = "Участники"; +"screen_room_change_role_unsaved_changes_title" = "Сохранить изменения?"; "screen_room_details_invite_people_title" = "Пригласить в комнату"; "screen_room_details_leave_conversation_title" = "Покинуть беседу"; "screen_room_details_leave_room_title" = "Покинуть комнату"; +"screen_room_details_notification_title" = "Уведомления"; "screen_room_details_roles_and_permissions" = "Роли и разрешения"; "screen_room_details_room_name_label" = "Название комнаты"; "screen_room_details_security_title" = "Безопасность"; "screen_room_details_topic_title" = "Тема"; "screen_room_error_failed_processing_media" = "Не удалось обработать медиафайл для загрузки, попробуйте еще раз."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Удалить и заблокировать участника"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Только упоминания и ключевые слова"; "screen_roomlist_filter_people" = "Люди"; +"screen_server_confirmation_change_server" = "Сменить поставщика учетной записи"; "screen_signout_confirmation_dialog_submit" = "Выйти"; "screen_signout_confirmation_dialog_title" = "Выйти"; +"screen_signout_key_backup_offline_title" = "Резервное копирование ключей все еще продолжается"; "screen_signout_preference_item" = "Выйти"; +"screen_signout_save_recovery_key_title" = "Вы сохранили ключ восстановления?"; +"troubleshoot_notifications_entry_point_title" = "Уведомления об устранении неполадок"; diff --git a/ElementX/Resources/Localizations/sk.lproj/Localizable.strings b/ElementX/Resources/Localizations/sk.lproj/Localizable.strings index 326537163a..d982ecb5bb 100644 --- a/ElementX/Resources/Localizations/sk.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/sk.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Pozastaviť"; "a11y_pin_field" = "Pole PIN"; "a11y_play" = "Prehrať"; -"a11y_poll" = "Anketa"; "a11y_poll_end" = "Ukončená anketa"; "a11y_react_with" = "Reagovať s %1$@"; "a11y_react_with_other_emojis" = "Reagovať s inými emotikonmi"; @@ -41,6 +40,7 @@ "action_create" = "Vytvoriť"; "action_create_a_room" = "Vytvoriť miestnosť"; "action_deactivate" = "Deaktivovať"; +"action_deactivate_account" = "Deaktivovať účet"; "action_decline" = "Odmietnuť"; "action_delete_poll" = "Odstrániť anketu"; "action_disable" = "Vypnúť"; @@ -64,6 +64,7 @@ "action_leave" = "Opustiť"; "action_leave_conversation" = "Opustiť konverzáciu"; "action_leave_room" = "Opustiť miestnosť"; +"action_load_more" = "Načítať viac"; "action_manage_account" = "Spravovať účet"; "action_manage_devices" = "Spravovať zariadenia"; "action_message" = "Poslať správu"; @@ -93,6 +94,7 @@ "action_send_message" = "Odoslať správu"; "action_share" = "Zdieľať"; "action_share_link" = "Zdieľať odkaz"; +"action_show" = "Zobraziť"; "action_sign_in_again" = "Prihláste sa znova"; "action_signout" = "Odhlásiť sa"; "action_signout_anyway" = "Napriek tomu sa odhlásiť"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "Zobraziť na časovej osi"; "action_view_source" = "Zobraziť zdroj"; "action_yes" = "Áno"; -"action.load_more" = "Načítať viac"; -"action_deactivate_account" = "Deaktivovať účet"; "banner_migrate_to_native_sliding_sync_action" = "Odhlásiť sa a aktualizovať"; "banner_migrate_to_native_sliding_sync_description" = "Váš server teraz podporuje nový, rýchlejší protokol. Odhláste sa a prihláste sa znova, aby ste mohli aktualizovať. Ak to urobíte teraz, pomôže vám vyhnúť sa nútenému odhláseniu, keď sa starý protokol neskôr odstráni."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Váš domovský server už nepodporuje starý protokol. Ak chcete pokračovať v používaní aplikácie, odhláste sa a znova sa prihláste."; @@ -162,6 +162,7 @@ "common_modern" = "Moderné"; "common_mute" = "Stlmiť"; "common_no_results" = "Žiadne výsledky"; +"common_no_room_name" = "Žiadny názov miestnosti"; "common_offline" = "Offline"; "common_optic_id_ios" = "Optic ID"; "common_or" = "alebo"; @@ -170,6 +171,8 @@ "common_permalink" = "Trvalý odkaz"; "common_permission" = "Povolenie"; "common_please_wait" = "Prosím, počkajte..."; +"common_poll_end_confirmation" = "Ste si istí, že chcete ukončiť túto anketu?"; +"common_poll_summary" = "Anketa: %1$@"; "common_poll_total_votes" = "Celkový počet hlasov: %1$@"; "common_poll_undisclosed_text" = "Výsledky sa zobrazia po ukončení ankety"; "common_privacy_policy" = "Zásady ochrany osobných údajov"; @@ -200,6 +203,7 @@ "common_settings" = "Nastavenia"; "common_shared_location" = "Zdieľaná poloha"; "common_signing_out" = "Odhlasovanie"; +"common_something_went_wrong" = "Niečo sa pokazilo"; "common_starting_chat" = "Spustenie konverzácie..."; "common_sticker" = "Nálepka"; "common_success" = "Úspech"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "O čom je táto miestnosť?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Nie je možné dešifrovať"; +"common_unable_to_decrypt_no_access" = "Nemáte prístup k tejto správe"; "common_unable_to_invite_message" = "Pozvánky nebolo možné odoslať jednému alebo viacerým používateľom."; "common_unable_to_invite_title" = "Nie je možné odoslať pozvánku/ky"; "common_unlock" = "Odomknúť"; @@ -221,24 +226,21 @@ "common_username" = "Používateľské meno"; "common_verification_cancelled" = "Overovanie zrušené"; "common_verification_complete" = "Overovanie je dokončené"; +"common_verify_device" = "Overiť zariadenie"; "common_video" = "Video"; "common_voice_message" = "Hlasová správa"; "common_waiting" = "Čaká sa…"; "common_waiting_for_decryption_key" = "Čaká sa na dešifrovací kľúč"; +"common.copied_to_clipboard" = "Skopírované do schránky"; "common.do_not_show_this_again" = "Nezobrazovať toto znova"; "common.open_source_licenses" = "Licencie s otvoreným zdrojom"; "common.pinned" = "Pripnuté"; "common.send_to" = "Odoslať"; "common.you" = "Vy"; -"common_no_room_name" = "Žiadny názov miestnosti"; -"common_poll_end_confirmation" = "Ste si istí, že chcete ukončiť túto anketu?"; -"common_poll_summary" = "Anketa: %1$@"; -"common_something_went_wrong" = "Niečo sa pokazilo"; -"common_unable_to_decrypt_no_access" = "Nemáte prístup k tejto správe"; -"common_verify_device" = "Overiť zariadenie"; "confirm_recovery_key_banner_message" = "Vaša záloha konverzácie nie je momentálne synchronizovaná. Na zachovanie prístupu k zálohe konverzácie musíte potvrdiť svoj kľúč na obnovu."; "confirm_recovery_key_banner_title" = "Potvrďte svoj kľúč na obnovenie"; "crash_detection_dialog_content" = "%1$@ zlyhal pri poslednom použití. Chcete zdieľať správu o páde s našim tímom?"; +"crypto_identity_change_pin_violation" = "Zdá sa, že totožnosť používateľa %1$@ sa zmenila.%2$@"; "dialog_permission_camera" = "Aby aplikácia mohla používať fotoaparát, udeľte povolenie v systémových nastaveniach."; "dialog_permission_generic" = "Udeľte prosím povolenie v systémových nastaveniach."; "dialog_permission_location_description_ios" = "Udeľte prístup v časti Nastavenia -> Poloha."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "Tiché oznámenia"; "notification_incoming_call" = "Prichádzajúci hovor"; "notification_inline_reply_failed" = "** Nepodarilo sa odoslať - prosím otvorte miestnosť"; -"notification_invitation_action_reject" = "Zamietnuť"; "notification_invite_body" = "Vás pozval/a na konverzáciu"; "notification_invite_body_with_sender" = "%1$@ vás pozval/a na rozhovor"; "notification_mentioned_you_body" = "Spomenul/a vás: %1$@"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "Neplatná adresa URL, uistite sa, že ste uviedli protokol (http/https) a správnu adresu."; "screen_pinned_timeline_empty_state_description" = "Stlačte správu a vyberte možnosť „%1$@“, ktorú chcete zahrnúť sem."; "screen_pinned_timeline_empty_state_headline" = "Pripnite dôležité správy, aby sa dali ľahko nájsť"; -"screen_pinned_timeline_screen_title_empty" = "Pripnuté správy"; "screen_reset_encryption_password_error" = "Nastala neznáma chyba. Skontrolujte, či je heslo vášho účtu správne a skúste to znova."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Odvolať overenie a odoslať"; "screen_resolve_send_failure_changed_identity_subtitle" = "Svoje overenie môžete odvolať a odoslať túto správu aj tak, alebo ju môžete zatiaľ zrušiť a po opätovnom overení to skúsiť znova %1$@ ."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Správa nebola odoslaná, pretože sa zmenila overená totožnosť používateľa %1$@."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Správa nebola odoslaná, pretože %1$@ neoveril/a všetky zariadenia."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Správa nebola odoslaná, pretože ste neoverili jedno alebo viac svojich zariadení."; -"screen_account_provider_change" = "Zmeniť poskytovateľa účtu"; "screen_account_provider_form_hint" = "Adresa domovského servera"; "screen_account_provider_form_notice" = "Zadajte hľadaný výraz alebo adresu domény."; "screen_account_provider_form_subtitle" = "Vyhľadať spoločnosť, komunitu alebo súkromný server."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "Zálohovanie zaisťuje, že nestratíte históriu správ. %1$@."; "screen_chat_backup_key_backup_title" = "Zálohovanie"; "screen_chat_backup_recovery_action_change" = "Zmeniť kľúč na obnovenie"; -"screen_chat_backup_recovery_action_confirm" = "Potvrdiť kľúč na obnovenie"; "screen_chat_backup_recovery_action_confirm_description" = "Vaša záloha konverzácie nie je momentálne synchronizovaná."; "screen_chat_backup_recovery_action_setup" = "Nastaviť obnovovanie"; "screen_chat_backup_recovery_action_setup_description" = "Získajte prístup k vašim šifrovaným správam aj keď stratíte všetky svoje zariadenia alebo sa odhlásite zo všetkých %1$@ zariadení."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Zobraziť výsledky až po skončení ankety"; "screen_create_poll_anonymous_headline" = "Anonymná anketa"; "screen_create_poll_answer_hint" = "Možnosť %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Vaše zmeny nebudú uložené"; "screen_create_poll_cancel_confirmation_title_ios" = "Zrušiť anketu"; "screen_create_poll_question_desc" = "Otázka alebo téma"; "screen_create_poll_question_hint" = "O čom je anketa?"; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Skupinové rozhovory"; "screen_notification_settings_invite_for_me_label" = "Pozvánky"; "screen_notification_settings_mentions_only_disclaimer" = "Váš domovský server nepodporuje túto možnosť v šifrovaných miestnostiach, v niektorých miestnostiach nemusíte dostať upozornenie."; -"screen_notification_settings_mentions_section_title" = "Zmienky"; "screen_notification_settings_mode_all" = "Všetky"; "screen_notification_settings_mode_mentions" = "Zmienky"; "screen_notification_settings_notification_section_title" = "Upozorniť ma na"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Zadať..."; "screen_recovery_key_confirm_lost_recovery_key" = "Stratili ste kľúč na obnovenie?"; "screen_recovery_key_confirm_success" = "Kľúč na obnovu potvrdený"; -"screen_recovery_key_confirm_title" = "Zadajte kľúč na obnovenie"; "screen_recovery_key_copied_to_clipboard" = "Skopírovaný kľúč na obnovenie"; "screen_recovery_key_generating_key" = "Generovanie..."; "screen_recovery_key_save_action" = "Uložiť kľúč na obnovenie"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Áno, znovu nastaviť teraz"; "screen_reset_encryption_confirmation_alert_subtitle" = "Tento proces je nezvratný."; "screen_reset_encryption_confirmation_alert_title" = "Naozaj chcete obnoviť svoje šifrovanie?"; -"screen_reset_encryption_password_placeholder" = "Zadajte..."; "screen_reset_encryption_password_subtitle" = "Potvrďte, že chcete obnoviť svoje šifrovanie."; "screen_reset_encryption_password_title" = "Ak chcete pokračovať, zadajte heslo účtu"; "screen_reset_identity_confirmation_subtitle" = "Chystáte sa prejsť na svoj %1$@ účet, aby ste obnovili svoju identitu. Potom budete vrátení späť do aplikácie."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Správcovia majú automaticky oprávnenia moderátora"; "screen_room_change_role_moderators_title" = "Upraviť moderátorov"; "screen_room_change_role_unsaved_changes_description" = "Máte neuložené zmeny."; -"screen_room_change_role_unsaved_changes_title" = "Uložiť zmeny?"; "screen_room_details_add_topic_title" = "Pridať tému"; "screen_room_details_already_a_member" = "Už ste členom"; "screen_room_details_already_invited" = "Už ste pozvaní"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Nepodarilo sa zrušiť stlmenie tejto miestnosti, skúste to prosím znova."; "screen_room_details_notification_mode_custom" = "Vlastné"; "screen_room_details_notification_mode_default" = "Predvolené"; -"screen_room_details_notification_title" = "Oznámenia"; "screen_room_details_share_room_title" = "Zdieľať miestnosť"; "screen_room_details_title" = "Informácie o miestnosti"; "screen_room_details_updating_room" = "Aktualizácia miestnosti..."; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Zakazuje sa %1$@"; "screen_room_member_list_manage_member_ban" = "Odstrániť a zakázať člena"; "screen_room_member_list_manage_member_remove" = "Odstrániť z miestnosti"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Odstrániť a zakázať člena"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Iba odstrániť člena"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Odstrániť člena a zakázať vstup v budúcnosti?"; "screen_room_member_list_manage_member_unban_action" = "Zrušiť zákaz"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Označiť ako prečítané"; "screen_roomlist_mark_as_unread" = "Označiť ako neprečítané"; "screen_roomlist_room_directory_button_title" = "Prehliadať všetky miestnosti"; -"screen_server_confirmation_change_server" = "Zmeniť poskytovateľa účtu"; "screen_server_confirmation_message_login_element_dot_io" = "Súkromný server pre zamestnancov spoločnosti Element."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix je otvorená sieť pre bezpečnú a decentralizovanú komunikáciu."; "screen_server_confirmation_message_register" = "Tu budú žiť vaše konverzácie - podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Chystáte sa odhlásiť z vašej poslednej relácie. Ak sa teraz odhlásite, stratíte prístup k svojim šifrovaným správam."; "screen_signout_key_backup_disabled_title" = "Vypli ste zálohovanie"; "screen_signout_key_backup_offline_subtitle" = "Keď ste sa odpojili od internetu, vaše kľúče sa ešte stále zálohovali. Pripojte sa znova k internetu, aby sa vaše kľúče mohli zálohovať pred odhlásením."; -"screen_signout_key_backup_offline_title" = "Vaše kľúče sa ešte stále zálohujú"; "screen_signout_key_backup_ongoing_subtitle" = "Pred odhlásením počkajte, kým sa to dokončí."; "screen_signout_key_backup_ongoing_title" = "Vaše kľúče sa ešte stále zálohujú"; "screen_signout_recovery_disabled_subtitle" = "Chystáte sa odhlásiť z vašej poslednej relácie. Ak sa teraz odhlásite, stratíte prístup k svojim šifrovaným správam."; "screen_signout_recovery_disabled_title" = "Obnovenie nie je nastavené"; "screen_signout_save_recovery_key_subtitle" = "Chystáte sa odhlásiť z vašej poslednej relácie. Ak sa teraz odhlásite, môžete stratiť prístup k svojim šifrovaným správam."; -"screen_signout_save_recovery_key_title" = "Uložili ste si kľúč na obnovenie?"; "screen_start_chat_error_starting_chat" = "Pri pokuse o spustenie konverzácie sa vyskytla chyba"; "screen_view_location_title" = "Poloha"; "screen_welcome_bullet_1" = "Hovory, ankety, vyhľadávanie a ďalšie funkcie pribudnú neskôr v tomto roku."; @@ -920,7 +908,6 @@ "test_language_identifier" = "sk"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Riešenie problémov"; -"troubleshoot_notifications_entry_point_title" = "Oznámenia riešení problémov"; "troubleshoot_notifications_screen_action" = "Spustiť testy"; "troubleshoot_notifications_screen_action_again" = "Spustiť testy znova"; "troubleshoot_notifications_screen_failure" = "Niektoré testy zlyhali. Skontrolujte prosím podrobnosti."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Uistite sa, že sú dostupní distribútori UnifiedPush."; "troubleshoot_notifications_test_unified_push_failure" = "Nenašli sa žiadni distribútori push."; "troubleshoot_notifications_test_unified_push_title" = "Skontrolovať UnifiedPush"; +"a11y_poll" = "Anketa"; "dialog_title_error" = "Chyba"; "dialog_title_success" = "Úspech"; "notification_fallback_content" = "Oznámenie"; "notification_invitation_action_join" = "Pripojiť sa"; +"notification_invitation_action_reject" = "Odmietnuť"; "notification_room_action_mark_as_read" = "Označiť ako prečítané"; "notification_room_action_quick_reply" = "Rýchla odpoveď"; +"screen_pinned_timeline_screen_title_empty" = "Pripnuté správy"; "screen_room_mentions_at_room_title" = "Všetci"; +"screen_account_provider_change" = "Zmeniť poskytovateľa účtu"; "screen_account_provider_signin_subtitle" = "Tu budú žiť vaše konverzácie - podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov."; "screen_account_provider_signup_subtitle" = "Tu budú žiť vaše konverzácie - podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov."; "screen_analytics_settings_help_us_improve" = "Zdieľajte anonymné údaje o používaní, aby sme mohli identifikovať problémy."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Všetky správy od nich budete môcť opäť vidieť."; "screen_blocked_users_unblock_alert_title" = "Odblokovať používateľa"; "screen_bug_report_rash_logs_alert_title" = "%1$@ zlyhal pri poslednom použití. Chcete zdieľať správu o páde s našim tímom?"; +"screen_chat_backup_recovery_action_confirm" = "Zadajte kľúč na obnovenie"; +"screen_create_poll_cancel_confirmation_content_ios" = "Vaše zmeny nebudú uložené"; "screen_create_room_add_people_title" = "Pozvať ľudí"; "screen_create_room_room_name_label" = "Názov miestnosti"; "screen_create_room_title" = "Vytvoriť miestnosť"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Upraviť anketu"; "screen_identity_use_another_device" = "Použite iné zariadenie"; "screen_login_subtitle" = "Matrix je otvorená sieť pre bezpečnú a decentralizovanú komunikáciu."; +"screen_notification_settings_mentions_section_title" = "Zmienky"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Skúste to znova"; +"screen_recovery_key_confirm_title" = "Potvrďte svoj kľúč na obnovenie"; "screen_report_content_block_user" = "Zablokovať používateľa"; +"screen_reset_encryption_password_placeholder" = "Zadať..."; "screen_room_attachment_source_camera_photo" = "Urobiť fotku"; "screen_room_change_permissions_everyone" = "Všetci"; "screen_room_change_permissions_member_moderation" = "Moderovanie členov"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Správcovia"; "screen_room_change_role_section_moderators" = "Moderátori"; "screen_room_change_role_section_users" = "Členovia"; +"screen_room_change_role_unsaved_changes_title" = "Uložiť zmeny?"; "screen_room_details_invite_people_title" = "Pozvať ľudí"; "screen_room_details_leave_conversation_title" = "Opustiť konverzáciu"; "screen_room_details_leave_room_title" = "Opustiť miestnosť"; +"screen_room_details_notification_title" = "Oznámenia"; "screen_room_details_roles_and_permissions" = "Roly a povolenia"; "screen_room_details_room_name_label" = "Názov miestnosti"; "screen_room_details_security_title" = "Bezpečnosť"; "screen_room_details_topic_title" = "Téma"; "screen_room_error_failed_processing_media" = "Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Odstrániť a zakázať člena"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Iba zmienky a kľúčové slová"; "screen_roomlist_filter_people" = "Ľudia"; +"screen_server_confirmation_change_server" = "Zmeniť poskytovateľa účtu"; "screen_signout_confirmation_dialog_submit" = "Odhlásiť sa"; "screen_signout_confirmation_dialog_title" = "Odhlásiť sa"; +"screen_signout_key_backup_offline_title" = "Vaše kľúče sa ešte stále zálohujú"; "screen_signout_preference_item" = "Odhlásiť sa"; +"screen_signout_save_recovery_key_title" = "Uložili ste kľúč na obnovenie?"; +"troubleshoot_notifications_entry_point_title" = "Oznámenia riešení problémov"; diff --git a/ElementX/Resources/Localizations/sv.lproj/Localizable.strings b/ElementX/Resources/Localizations/sv.lproj/Localizable.strings index 71daaf32ea..b8a62974c3 100644 --- a/ElementX/Resources/Localizations/sv.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/sv.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Pausa"; "a11y_pin_field" = "PIN-fält"; "a11y_play" = "Spela upp"; -"a11y_poll" = "Omröstning"; "a11y_poll_end" = "Avslutade omröstning"; "a11y_react_with" = "Reagera med %1$@"; "a11y_react_with_other_emojis" = "Reagera med andra emojier"; @@ -41,6 +40,7 @@ "action_create" = "Skapa"; "action_create_a_room" = "Skapa ett rum"; "action_deactivate" = "Deactivate"; +"action_deactivate_account" = "Deactivate account"; "action_decline" = "Neka"; "action_delete_poll" = "Radera omröstning"; "action_disable" = "Inaktivera"; @@ -64,6 +64,7 @@ "action_leave" = "Lämna"; "action_leave_conversation" = "Lämna konversation"; "action_leave_room" = "Lämna rum"; +"action_load_more" = "Ladda mer"; "action_manage_account" = "Hantera konto"; "action_manage_devices" = "Hantera enheter"; "action_message" = "Meddela"; @@ -93,6 +94,7 @@ "action_send_message" = "Skicka meddelande"; "action_share" = "Dela"; "action_share_link" = "Dela länk"; +"action_show" = "Show"; "action_sign_in_again" = "Logga in igen"; "action_signout" = "Logga ut"; "action_signout_anyway" = "Logga ut ändå"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "Visa i tidslinjen"; "action_view_source" = "Visa källkod"; "action_yes" = "Ja"; -"action.load_more" = "Ladda mer"; -"action_deactivate_account" = "Deactivate account"; "banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; @@ -162,6 +162,7 @@ "common_modern" = "Modernt"; "common_mute" = "Tysta"; "common_no_results" = "Inga resultat"; +"common_no_room_name" = "Inget rumsnamn"; "common_offline" = "Frånkopplad"; "common_optic_id_ios" = "Optic ID"; "common_or" = "eller"; @@ -170,6 +171,8 @@ "common_permalink" = "Permalänk"; "common_permission" = "Behörighet"; "common_please_wait" = "Vänligen vänta …"; +"common_poll_end_confirmation" = "Är du säker på att du vill avsluta den här omröstningen?"; +"common_poll_summary" = "Omröstning: %1$@"; "common_poll_total_votes" = "Totalt antal röster: %1$@"; "common_poll_undisclosed_text" = "Resultaten visas efter att omröstningen har avslutats"; "common_privacy_policy" = "Integritetspolicy"; @@ -200,6 +203,7 @@ "common_settings" = "Inställningar"; "common_shared_location" = "Delade plats"; "common_signing_out" = "Loggar ut"; +"common_something_went_wrong" = "Något gick fel"; "common_starting_chat" = "Startar chatt …"; "common_sticker" = "Dekal"; "common_success" = "Lyckades"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "Vad handlar det här rummet om?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Kan inte avkryptera"; +"common_unable_to_decrypt_no_access" = "Du har inte tillgång till det här meddelandet"; "common_unable_to_invite_message" = "Inbjudan kunde inte skickas till en eller flera användare."; "common_unable_to_invite_title" = "Kunde inte skicka inbjudningar"; "common_unlock" = "Lås upp"; @@ -221,24 +226,21 @@ "common_username" = "Användarnamn"; "common_verification_cancelled" = "Verifiering avbruten"; "common_verification_complete" = "Verifieringen slutförd"; +"common_verify_device" = "Verifiera enheten"; "common_video" = "Video"; "common_voice_message" = "Röstmeddelande"; "common_waiting" = "Väntar …"; "common_waiting_for_decryption_key" = "Väntar på detta meddelande"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "Visa inte detta igen"; "common.open_source_licenses" = "Licenser för öppen källkod"; "common.pinned" = "Fäst"; "common.send_to" = "Skicka till"; "common.you" = "You"; -"common_no_room_name" = "Inget rumsnamn"; -"common_poll_end_confirmation" = "Är du säker på att du vill avsluta den här omröstningen?"; -"common_poll_summary" = "Omröstning: %1$@"; -"common_something_went_wrong" = "Något gick fel"; -"common_unable_to_decrypt_no_access" = "Du har inte tillgång till det här meddelandet"; -"common_verify_device" = "Verifiera enheten"; "confirm_recovery_key_banner_message" = "Din chattsäkerhetskopia är för närvarande inte synkroniserad. Du måste ange din återställningsnyckel för att behålla åtkomsten till din chattsäkerhetskopia."; "confirm_recovery_key_banner_title" = "Ange din återställningsnyckel"; "crash_detection_dialog_content" = "%1$@ kraschade senast den användes. Vill du dela en kraschrapport med oss?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "För att låta programmet använda kameran, vänligen ge behörigheten i systeminställningarna."; "dialog_permission_generic" = "Vänligen ge behörigheten i systeminställningarna."; "dialog_permission_location_description_ios" = "Ge åtkomst i Inställningar -> Plats."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "Tysta aviseringar"; "notification_incoming_call" = "Inkommande samtal"; "notification_inline_reply_failed" = "** Misslyckades att skicka - vänligen öppna rummet"; -"notification_invitation_action_reject" = "Avvisa"; "notification_invite_body" = "Bjöd in dig att chatta"; "notification_invite_body_with_sender" = "%1$@ invited you to chat"; "notification_mentioned_you_body" = "Nämnde dig: %1$@"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "Ogiltig URL, se till att du inkluderar protokollet (http/https) och rätt adress."; "screen_pinned_timeline_empty_state_description" = "Tryck på ett meddelande och välj ”%1$@” för att inkludera det här."; "screen_pinned_timeline_empty_state_headline" = "Fäst viktiga meddelanden så att de lätt kan upptäckas"; -"screen_pinned_timeline_screen_title_empty" = "Fästa meddelanden"; "screen_reset_encryption_password_error" = "Ett okänt fel inträffade. Kontrollera att ditt kontolösenord är korrekt och försök igen."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; "screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "Byt kontoleverantör"; "screen_account_provider_form_hint" = "Hemserveradress"; "screen_account_provider_form_notice" = "Ange ett sökord eller en domänadress."; "screen_account_provider_form_subtitle" = "Sök efter ett företag, en gemenskap eller en privat server."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "Säkerhetskopior ser till att du inte blir av med din meddelandehistorik. %1$@."; "screen_chat_backup_key_backup_title" = "Säkerhetskopia"; "screen_chat_backup_recovery_action_change" = "Byt återställningsnyckel"; -"screen_chat_backup_recovery_action_confirm" = "Ange återställningsnyckel"; "screen_chat_backup_recovery_action_confirm_description" = "Din chattsäkerhetskopia är för närvarande osynkroniserad."; "screen_chat_backup_recovery_action_setup" = "Ställ in återställning"; "screen_chat_backup_recovery_action_setup_description" = "Få tillgång till dina krypterade meddelanden om du tappar bort alla dina enheter eller blir utloggad ur %1$@ överallt."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Visa resultat först efter att omröstningen avslutats"; "screen_create_poll_anonymous_headline" = "Dölj röster"; "screen_create_poll_answer_hint" = "Alternativ %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Dina ändringar sparas inte"; "screen_create_poll_cancel_confirmation_title_ios" = "Avbryt omröstning"; "screen_create_poll_question_desc" = "Fråga eller ämne"; "screen_create_poll_question_hint" = "Vad handlar omröstningen om?"; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Gruppchattar"; "screen_notification_settings_invite_for_me_label" = "Inbjudningar"; "screen_notification_settings_mentions_only_disclaimer" = "Din hemserver stöder inte det här alternativet i krypterade rum, du kanske inte aviseras i vissa rum."; -"screen_notification_settings_mentions_section_title" = "Omnämnanden"; "screen_notification_settings_mode_all" = "Alla"; "screen_notification_settings_mode_mentions" = "Omnämnanden"; "screen_notification_settings_notification_section_title" = "Meddela mig för"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Ange …"; "screen_recovery_key_confirm_lost_recovery_key" = "Blivit av med din återställningsnyckel?"; "screen_recovery_key_confirm_success" = "Återställningsnyckel bekräftad"; -"screen_recovery_key_confirm_title" = "Ange din återställningsnyckel"; "screen_recovery_key_copied_to_clipboard" = "Kopierade återställningsnyckel"; "screen_recovery_key_generating_key" = "Genererar …"; "screen_recovery_key_save_action" = "Spara återställningsnyckeln"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Ja, återställ nu"; "screen_reset_encryption_confirmation_alert_subtitle" = "Denna process är irreversibel."; "screen_reset_encryption_confirmation_alert_title" = "Är du säker på att du vill återställa din kryptering?"; -"screen_reset_encryption_password_placeholder" = "Ange …"; "screen_reset_encryption_password_subtitle" = "Bekräfta att du vill återställa din kryptering."; "screen_reset_encryption_password_title" = "Ange ditt kontolösenord för att fortsätta"; "screen_reset_identity_confirmation_subtitle" = "Du är på väg att gå till ditt %1$@-konto för att återställa din identitet. Därefter kommer du att tas tillbaka till appen."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Administratörer har automatiskt moderatorbehörighet"; "screen_room_change_role_moderators_title" = "Redigera moderatorer"; "screen_room_change_role_unsaved_changes_description" = "Du har osparade ändringar."; -"screen_room_change_role_unsaved_changes_title" = "Spara ändringar?"; "screen_room_details_add_topic_title" = "Lägg till ämne"; "screen_room_details_already_a_member" = "Redan medlem"; "screen_room_details_already_invited" = "Redan inbjuden"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Misslyckades att avtysta det här rummet, vänligen pröva igen."; "screen_room_details_notification_mode_custom" = "Anpassad"; "screen_room_details_notification_mode_default" = "Förval"; -"screen_room_details_notification_title" = "Aviseringar"; "screen_room_details_share_room_title" = "Dela rum"; "screen_room_details_title" = "Rumsinfo"; "screen_room_details_updating_room" = "Uppdaterar rummet …"; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Bannar %1$@"; "screen_room_member_list_manage_member_ban" = "Ta bort och banna medlem"; "screen_room_member_list_manage_member_remove" = "Ta bort från rummet"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Ta bort och banna medlem"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Ta bara bort medlem"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Ta bort medlem och banna från att gå med i framtiden?"; "screen_room_member_list_manage_member_unban_action" = "Avbanna"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Markera som läst"; "screen_roomlist_mark_as_unread" = "Markera som oläst"; "screen_roomlist_room_directory_button_title" = "Bläddra bland alla rum"; -"screen_server_confirmation_change_server" = "Byt kontoleverantör"; "screen_server_confirmation_message_login_element_dot_io" = "En privat server för Element-anställda."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix är ett öppet nätverk för säker, decentraliserad kommunikation."; "screen_server_confirmation_message_register" = "Det är här dina konversationer kommer att sparas - precis som du skulle använda en e-postleverantör för att spara dina e-brev."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Du är på väg att logga ut ur din senaste session. Om du loggar ut nu kommer du att förlora åtkomsten till dina krypterade meddelanden."; "screen_signout_key_backup_disabled_title" = "Du har stängt av säkerhetskopiering"; "screen_signout_key_backup_offline_subtitle" = "Dina nycklar säkerhetskopierades fortfarande när du gick offline. Anslut igen så att dina nycklar kan säkerhetskopieras innan du loggar ut."; -"screen_signout_key_backup_offline_title" = "Dina nycklar säkerhetskopieras fortfarande"; "screen_signout_key_backup_ongoing_subtitle" = "Vänta tills detta är klart innan du loggar ut."; "screen_signout_key_backup_ongoing_title" = "Dina nycklar säkerhetskopieras fortfarande"; "screen_signout_recovery_disabled_subtitle" = "Du är på väg att logga ut ur din sista session. Om du loggar ut nu förlorar du åtkomsten till dina krypterade meddelanden."; "screen_signout_recovery_disabled_title" = "Återställning inte inställd"; "screen_signout_save_recovery_key_subtitle" = "Du är på väg att logga ut från din senaste session. Om du loggar ut nu kan du förlora åtkomsten till dina krypterade meddelanden."; -"screen_signout_save_recovery_key_title" = "Har du sparat din återställningsnyckel?"; "screen_start_chat_error_starting_chat" = "Ett fel uppstod när du försökte starta en chatt"; "screen_view_location_title" = "Plats"; "screen_welcome_bullet_1" = "Samtal, omröstningar, sökning och mer kommer att läggas till senare i år."; @@ -920,7 +908,6 @@ "test_language_identifier" = "sv"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Felsök"; -"troubleshoot_notifications_entry_point_title" = "Felsök aviseringar"; "troubleshoot_notifications_screen_action" = "Kör tester"; "troubleshoot_notifications_screen_action_again" = "Kör tester igen"; "troubleshoot_notifications_screen_failure" = "Vissa tester misslyckades. Kontrollera detaljerna."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Se till att UnifiedPush-distributörer är tillgängliga."; "troubleshoot_notifications_test_unified_push_failure" = "Inga push-distributörer hittades."; "troubleshoot_notifications_test_unified_push_title" = "Kontrollera UnifiedPush"; +"a11y_poll" = "Omröstning"; "dialog_title_error" = "Fel"; "dialog_title_success" = "Lyckades"; "notification_fallback_content" = "notis"; "notification_invitation_action_join" = "Gå med"; +"notification_invitation_action_reject" = "Avvisa"; "notification_room_action_mark_as_read" = "Markera som läst"; "notification_room_action_quick_reply" = "Snabbsvar"; +"screen_pinned_timeline_screen_title_empty" = "Fästa meddelanden"; "screen_room_mentions_at_room_title" = "Alla"; +"screen_account_provider_change" = "Byt kontoleverantör"; "screen_account_provider_signin_subtitle" = "Det är här dina konversationer kommer att sparas - precis som du skulle använda en e-postleverantör för att spara dina e-brev."; "screen_account_provider_signup_subtitle" = "Det är här dina konversationer kommer att sparas - precis som du skulle använda en e-postleverantör för att spara dina e-brev."; "screen_analytics_settings_help_us_improve" = "Dela anonyma användningsdata för att hjälpa oss att identifiera problem."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Du kommer att kunna se alla meddelanden från dem igen."; "screen_blocked_users_unblock_alert_title" = "Avblockera användare"; "screen_bug_report_rash_logs_alert_title" = "%1$@ kraschade senast den användes. Vill du dela en kraschrapport med oss?"; +"screen_chat_backup_recovery_action_confirm" = "Ange återställningsnyckel"; +"screen_create_poll_cancel_confirmation_content_ios" = "Dina ändringar kommer inte att sparas"; "screen_create_room_add_people_title" = "Bjud in personer"; "screen_create_room_room_name_label" = "Rumsnamn"; "screen_create_room_title" = "Skapa ett rum"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Redigera omröstning"; "screen_identity_use_another_device" = "Använd en annan enhet"; "screen_login_subtitle" = "Matrix är ett öppet nätverk för säker, decentraliserad kommunikation."; +"screen_notification_settings_mentions_section_title" = "Omnämnanden"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Försök igen"; +"screen_recovery_key_confirm_title" = "Ange din återställningsnyckel"; "screen_report_content_block_user" = "Blockera användare"; +"screen_reset_encryption_password_placeholder" = "Ange …"; "screen_room_attachment_source_camera_photo" = "Ta ett foto"; "screen_room_change_permissions_everyone" = "Alla"; "screen_room_change_permissions_member_moderation" = "Medlemsmoderering"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Administratörer"; "screen_room_change_role_section_moderators" = "Moderatorer"; "screen_room_change_role_section_users" = "Medlemmar"; +"screen_room_change_role_unsaved_changes_title" = "Spara ändringar?"; "screen_room_details_invite_people_title" = "Bjud in personer"; "screen_room_details_leave_conversation_title" = "Lämna konversation"; "screen_room_details_leave_room_title" = "Lämna rum"; +"screen_room_details_notification_title" = "Aviseringar"; "screen_room_details_roles_and_permissions" = "Roller och behörigheter"; "screen_room_details_room_name_label" = "Rumsnamn"; "screen_room_details_security_title" = "Säkerhet"; "screen_room_details_topic_title" = "Ämne"; "screen_room_error_failed_processing_media" = "Misslyckades att bearbeta media för uppladdning, vänligen pröva igen."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Ta bort och banna medlem"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Endast omnämnanden och nyckelord"; "screen_roomlist_filter_people" = "Personer"; +"screen_server_confirmation_change_server" = "Byt kontoleverantör"; "screen_signout_confirmation_dialog_submit" = "Logga ut"; "screen_signout_confirmation_dialog_title" = "Logga ut"; +"screen_signout_key_backup_offline_title" = "Dina nycklar säkerhetskopieras fortfarande"; "screen_signout_preference_item" = "Logga ut"; +"screen_signout_save_recovery_key_title" = "Har du sparat din återställningsnyckel?"; +"troubleshoot_notifications_entry_point_title" = "Felsök aviseringar"; diff --git a/ElementX/Resources/Localizations/uk.lproj/Localizable.strings b/ElementX/Resources/Localizations/uk.lproj/Localizable.strings index 8e6abc1384..c237425ac4 100644 --- a/ElementX/Resources/Localizations/uk.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/uk.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Пауза"; "a11y_pin_field" = "Поле PIN-коду"; "a11y_play" = "Відтворити"; -"a11y_poll" = "Опитування"; "a11y_poll_end" = "Опитування завершено"; "a11y_react_with" = "Реагувати з%1$@"; "a11y_react_with_other_emojis" = "Відреагувати іншими смайликами"; @@ -41,6 +40,7 @@ "action_create" = "Створити"; "action_create_a_room" = "Створити кімнату"; "action_deactivate" = "Deactivate"; +"action_deactivate_account" = "Deactivate account"; "action_decline" = "Відхилити"; "action_delete_poll" = "Видалити опитування"; "action_disable" = "Вимкнути"; @@ -64,6 +64,7 @@ "action_leave" = "Вийти"; "action_leave_conversation" = "Залишити розмову"; "action_leave_room" = "Вийти з кімнати"; +"action_load_more" = "Завантажити ще"; "action_manage_account" = "Керування обліковим записом"; "action_manage_devices" = "Керування пристроями"; "action_message" = "Написати"; @@ -93,6 +94,7 @@ "action_send_message" = "Надіслати повідомлення"; "action_share" = "Поділитися"; "action_share_link" = "Поширити посилання"; +"action_show" = "Show"; "action_sign_in_again" = "Увійдіть знову"; "action_signout" = "Вийти"; "action_signout_anyway" = "Все одно вийти"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "View in timeline"; "action_view_source" = "Переглянути джерело"; "action_yes" = "Так"; -"action.load_more" = "Завантажити ще"; -"action_deactivate_account" = "Deactivate account"; "banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; @@ -162,6 +162,7 @@ "common_modern" = "Модерн"; "common_mute" = "Вимкнути звук"; "common_no_results" = "Немає результатів"; +"common_no_room_name" = "Немає назви кімнати"; "common_offline" = "Не в мережі"; "common_optic_id_ios" = "Optic ID"; "common_or" = "або"; @@ -170,6 +171,8 @@ "common_permalink" = "Постійне посилання"; "common_permission" = "Дозвіл"; "common_please_wait" = "Будь ласка, зачекайте…"; +"common_poll_end_confirmation" = "Ви впевнені, що хочете закінчити це опитування?"; +"common_poll_summary" = "Опитування: %1$@"; "common_poll_total_votes" = "Всього голосів: %1$@"; "common_poll_undisclosed_text" = "Результати будуть показані після завершення опитування"; "common_privacy_policy" = "Політика конфіденційності"; @@ -200,6 +203,7 @@ "common_settings" = "Налаштування"; "common_shared_location" = "Поширене розташування"; "common_signing_out" = "Вихід"; +"common_something_went_wrong" = "Щось пішло не так"; "common_starting_chat" = "Початок чату..."; "common_sticker" = "Наліпка"; "common_success" = "Успіх"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "Про що ця кімната?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Неможливо розшифрувати"; +"common_unable_to_decrypt_no_access" = "Ви не маєте доступу до цього повідомлення"; "common_unable_to_invite_message" = "Не вдалося надіслати запрошення одному чи кільком користувачам."; "common_unable_to_invite_title" = "Не вдалося надіслати запрошення"; "common_unlock" = "Розблокувати"; @@ -221,24 +226,21 @@ "common_username" = "Ім'я користувача"; "common_verification_cancelled" = "Верифікацію скасовано"; "common_verification_complete" = "Верифікацію завершено"; +"common_verify_device" = "Перевірте пристрій"; "common_video" = "Відео"; "common_voice_message" = "Голосове повідомлення"; "common_waiting" = "Очікування..."; "common_waiting_for_decryption_key" = "Чекаємо на це повідомлення"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "Не показувати це знову"; "common.open_source_licenses" = "Ліцензії відкритого коду"; "common.pinned" = "Pinned"; "common.send_to" = "Надіслати до"; "common.you" = "You"; -"common_no_room_name" = "Немає назви кімнати"; -"common_poll_end_confirmation" = "Ви впевнені, що хочете закінчити це опитування?"; -"common_poll_summary" = "Опитування: %1$@"; -"common_something_went_wrong" = "Щось пішло не так"; -"common_unable_to_decrypt_no_access" = "Ви не маєте доступу до цього повідомлення"; -"common_verify_device" = "Перевірте пристрій"; "confirm_recovery_key_banner_message" = "Ваша резервна копія чату наразі не синхронізована. Вам потрібно підтвердити ключ відновлення, щоб зберегти доступ до резервної копії чату."; "confirm_recovery_key_banner_title" = "Підтвердіть ключ відновлення"; "crash_detection_dialog_content" = "%1$@ аварійно завершив роботу під час останнього використання. Бажаєте поділитися з нами звітом про збій?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "Для того, щоб дозволити програмі використовувати камеру, надайте дозвіл у системних налаштуваннях."; "dialog_permission_generic" = "Будь ласка, надайте дозвіл в системних налаштуваннях."; "dialog_permission_location_description_ios" = "Надайте доступ в Налаштуваннях -> Місцезнаходження."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "Тихі сповіщення"; "notification_incoming_call" = "Вхідний дзвінок"; "notification_inline_reply_failed" = "** Не вдалося надіслати - будь ласка, відкрийте кімнату"; -"notification_invitation_action_reject" = "Відхилити"; "notification_invite_body" = "Запросив (-ла) Вас до чату"; "notification_invite_body_with_sender" = "%1$@ invited you to chat"; "notification_mentioned_you_body" = "Згадав(-ла) вас: %1$@"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "Неправильна URL-адреса, будь ласка, переконайтеся, що ви вказали протокол (http/https) та правильну адресу."; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; -"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; "screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "Змінити провайдера облікового запису"; "screen_account_provider_form_hint" = "Адреса домашнього сервера"; "screen_account_provider_form_notice" = "Уведіть пошуковий термін або адресу домену."; "screen_account_provider_form_subtitle" = "Пошук компанії, спільноти або приватного сервера."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "Резервне копіювання гарантує, що ви не втратите історію повідомлень. %1$@."; "screen_chat_backup_key_backup_title" = "Резервне копіювання"; "screen_chat_backup_recovery_action_change" = "Змінити ключ відновлення"; -"screen_chat_backup_recovery_action_confirm" = "Підтвердити ключ відновлення"; "screen_chat_backup_recovery_action_confirm_description" = "Ваша резервна копія чату наразі не синхронізована."; "screen_chat_backup_recovery_action_setup" = "Налаштувати відновлення"; "screen_chat_backup_recovery_action_setup_description" = "Отримайте доступ до своїх зашифрованих повідомлень, якщо ви втратите всі свої пристрої або вийшли з %1$@ системи."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Показувати результати тільки після закінчення опитування"; "screen_create_poll_anonymous_headline" = "Приховати голоси"; "screen_create_poll_answer_hint" = "Варіант %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Ваші зміни не будуть збережені"; "screen_create_poll_cancel_confirmation_title_ios" = "Скасувати опитування"; "screen_create_poll_question_desc" = "Питання або тема"; "screen_create_poll_question_hint" = "Про що йдеться в опитуванні?"; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Групові чати"; "screen_notification_settings_invite_for_me_label" = "Запрошення"; "screen_notification_settings_mentions_only_disclaimer" = "Ваш домашній сервер не підтримує цю опцію в зашифрованих кімнатах, ви можете не отримати сповіщення в деяких кімнатах."; -"screen_notification_settings_mentions_section_title" = "Згадки"; "screen_notification_settings_mode_all" = "Усі"; "screen_notification_settings_mode_mentions" = "Згадки"; "screen_notification_settings_notification_section_title" = "Повідомляти мене про"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Ввести..."; "screen_recovery_key_confirm_lost_recovery_key" = "Загубили ключ відновлення?"; "screen_recovery_key_confirm_success" = "Ключ відновлення підтверджено"; -"screen_recovery_key_confirm_title" = "Підтвердіть ключ відновлення"; "screen_recovery_key_copied_to_clipboard" = "Скопійовано ключ відновлення"; "screen_recovery_key_generating_key" = "Створення…"; "screen_recovery_key_save_action" = "Зберегти ключ відновлення"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Так, скинути зараз"; "screen_reset_encryption_confirmation_alert_subtitle" = "Цей процес незворотний."; "screen_reset_encryption_confirmation_alert_title" = "Ви впевнені, що хочете скинути шифрування?"; -"screen_reset_encryption_password_placeholder" = "Ввести…"; "screen_reset_encryption_password_subtitle" = "Підтвердьте, що ви хочете скинути шифрування."; "screen_reset_encryption_password_title" = "Введіть пароль облікового запису, щоб продовжити"; "screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Адміністратори автоматично мають права модератора"; "screen_room_change_role_moderators_title" = "Керувати модераторами"; "screen_room_change_role_unsaved_changes_description" = "У вас є не збережені зміни."; -"screen_room_change_role_unsaved_changes_title" = "Зберегти зміни?"; "screen_room_details_add_topic_title" = "Додати тему"; "screen_room_details_already_a_member" = "Уже учасник"; "screen_room_details_already_invited" = "Уже запрошені"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Не вдалося ввімкнути звук цієї кімнати. Повторіть спробу."; "screen_room_details_notification_mode_custom" = "Власні"; "screen_room_details_notification_mode_default" = "За замовчуванням"; -"screen_room_details_notification_title" = "Сповіщення"; "screen_room_details_share_room_title" = "Поділитися кімнатою"; "screen_room_details_title" = "Інформація про кімнату"; "screen_room_details_updating_room" = "Оновлення кімнати..."; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Блокування %1$@"; "screen_room_member_list_manage_member_ban" = "Вилучити й заблокувати учасника"; "screen_room_member_list_manage_member_remove" = "Вилучити з кімнати"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Видалити та заблокувати учасника"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Лише видалити учасника"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Видалити учасника та заборонити приєднання в майбутньому?"; "screen_room_member_list_manage_member_unban_action" = "Розблокувати"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Позначити прочитаним"; "screen_roomlist_mark_as_unread" = "Позначити непрочитаним"; "screen_roomlist_room_directory_button_title" = "Переглянути всі кімнати"; -"screen_server_confirmation_change_server" = "Змінити провайдера облікового запису"; "screen_server_confirmation_message_login_element_dot_io" = "Приватний сервер для співробітників Element."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix — це відкрита мережа для безпечної, децентралізованої комунікації."; "screen_server_confirmation_message_register" = "Тут будуть зберігатися Ваші розмови - так само, як Ви використовуєте поштову скриньку для зберігання своїх електронних листів."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "Ви збираєтеся вийти зі свого останнього сеансу. Якщо ви вийдете зараз, ви втратите доступ до своїх зашифрованих повідомлень."; "screen_signout_key_backup_disabled_title" = "Ви вимкнули резервне копіювання"; "screen_signout_key_backup_offline_subtitle" = "Коли ви вийшли з мережі, резервна копія ваших ключів все ще створювалася. Повторно підключіться, щоб зберегти резервну копію ключів перед виходом з системи."; -"screen_signout_key_backup_offline_title" = "Резервне копіювання ваших ключів ще триває"; "screen_signout_key_backup_ongoing_subtitle" = "Зачекайте, поки це завершиться, перш ніж вийти."; "screen_signout_key_backup_ongoing_title" = "Резервне копіювання ваших ключів ще триває"; "screen_signout_recovery_disabled_subtitle" = "Ви збираєтеся вийти зі свого останнього сеансу. Якщо ви вийдете зараз, ви втратите доступ до своїх зашифрованих повідомлень."; "screen_signout_recovery_disabled_title" = "Відновлення не налаштовано"; "screen_signout_save_recovery_key_subtitle" = "Ви збираєтеся вийти зі свого останнього сеансу. Якщо вийти зараз, ви можете втратити доступ до зашифрованих повідомлень."; -"screen_signout_save_recovery_key_title" = "Ви зберегли ключ відновлення?"; "screen_start_chat_error_starting_chat" = "Під час спроби почати чат сталася помилка"; "screen_view_location_title" = "Місцезнаходження"; "screen_welcome_bullet_1" = "Дзвінки, опитування, пошук тощо будуть додані пізніше цього року."; @@ -920,7 +908,6 @@ "test_language_identifier" = "uk"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Усунення несправностей"; -"troubleshoot_notifications_entry_point_title" = "Усунення неполадок сповіщень"; "troubleshoot_notifications_screen_action" = "Запустити тести"; "troubleshoot_notifications_screen_action_again" = "Запустити тести знову"; "troubleshoot_notifications_screen_failure" = "Деякі тести не пройшли. Будь ласка, перегляньте деталі."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Переконується, що дистриб'ютори UnifiedPush доступні."; "troubleshoot_notifications_test_unified_push_failure" = "Дистриб'юторів не знайдено."; "troubleshoot_notifications_test_unified_push_title" = "Перевірка UnifiedPush"; +"a11y_poll" = "Опитування"; "dialog_title_error" = "Помилка"; "dialog_title_success" = "Успіх"; "notification_fallback_content" = "Сповіщення"; "notification_invitation_action_join" = "Доєднатися"; +"notification_invitation_action_reject" = "Відхилити"; "notification_room_action_mark_as_read" = "Позначити прочитаним"; "notification_room_action_quick_reply" = "Швидка відповідь"; +"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_room_mentions_at_room_title" = "Усі"; +"screen_account_provider_change" = "Змінити провайдера облікового запису"; "screen_account_provider_signin_subtitle" = "Тут будуть зберігатися Ваші розмови - так само, як Ви використовуєте поштову скриньку для зберігання своїх електронних листів."; "screen_account_provider_signup_subtitle" = "Тут будуть зберігатися Ваші розмови - так само, як Ви використовуєте поштову скриньку для зберігання своїх електронних листів."; "screen_analytics_settings_help_us_improve" = "Ділитися анонімними даними про використання, щоб допомогати нам виявляти проблеми."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Ви знову зможете бачити всі повідомлення від них."; "screen_blocked_users_unblock_alert_title" = "Розблокувати користувача"; "screen_bug_report_rash_logs_alert_title" = "%1$@ аварійно завершив роботу під час останнього використання. Бажаєте поділитися з нами звітом про збій?"; +"screen_chat_backup_recovery_action_confirm" = "Введіть ключ відновлення"; +"screen_create_poll_cancel_confirmation_content_ios" = "Внесені зміни не буде збережено"; "screen_create_room_add_people_title" = "Запросити людей"; "screen_create_room_room_name_label" = "Назва кімнати"; "screen_create_room_title" = "Створити кімнату"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "Редагувати опитування"; "screen_identity_use_another_device" = "Use another device"; "screen_login_subtitle" = "Matrix — це відкрита мережа для безпечної, децентралізованої комунікації."; +"screen_notification_settings_mentions_section_title" = "Згадки"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Спробуйте ще раз"; +"screen_recovery_key_confirm_title" = "Підтвердіть ключ відновлення"; "screen_report_content_block_user" = "Заблокувати користувача"; +"screen_reset_encryption_password_placeholder" = "Ввести..."; "screen_room_attachment_source_camera_photo" = "Зробити фото"; "screen_room_change_permissions_everyone" = "Усі"; "screen_room_change_permissions_member_moderation" = "Модерація учасників"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Адміністратори"; "screen_room_change_role_section_moderators" = "Модератори"; "screen_room_change_role_section_users" = "Учасники"; +"screen_room_change_role_unsaved_changes_title" = "Зберегти зміни?"; "screen_room_details_invite_people_title" = "Запросити людей"; "screen_room_details_leave_conversation_title" = "Залишити розмову"; "screen_room_details_leave_room_title" = "Вийти з кімнати"; +"screen_room_details_notification_title" = "Сповіщення"; "screen_room_details_roles_and_permissions" = "Ролі та дозволи"; "screen_room_details_room_name_label" = "Назва кімнати"; "screen_room_details_security_title" = "Безпека"; "screen_room_details_topic_title" = "Тема"; "screen_room_error_failed_processing_media" = "Не вдалося обробити медіафайл для завантаження, спробуйте ще раз."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Вилучити й заблокувати учасника"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Тільки згадки та ключові слова"; "screen_roomlist_filter_people" = "Люди"; +"screen_server_confirmation_change_server" = "Змінити провайдера облікового запису"; "screen_signout_confirmation_dialog_submit" = "Вийти"; "screen_signout_confirmation_dialog_title" = "Вийти"; +"screen_signout_key_backup_offline_title" = "Резервне копіювання ваших ключів ще триває"; "screen_signout_preference_item" = "Вийти"; +"screen_signout_save_recovery_key_title" = "Ви зберегли ключ відновлення?"; +"troubleshoot_notifications_entry_point_title" = "Усунення неполадок сповіщень"; diff --git a/ElementX/Resources/Localizations/uz.lproj/Localizable.strings b/ElementX/Resources/Localizations/uz.lproj/Localizable.strings index 3aa76c9957..a41ecfc4e9 100644 --- a/ElementX/Resources/Localizations/uz.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/uz.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "Pauza"; "a11y_pin_field" = "PIN field"; "a11y_play" = "O'ynang"; -"a11y_poll" = "So'ro'vnoma"; "a11y_poll_end" = "So‘rovnoma yakunlandi"; "a11y_react_with" = "React with %1$@"; "a11y_react_with_other_emojis" = "React with other emojis"; @@ -41,6 +40,7 @@ "action_create" = "Yaratmoq"; "action_create_a_room" = "Xonani yaratish"; "action_deactivate" = "Deactivate"; +"action_deactivate_account" = "Deactivate account"; "action_decline" = "Rad etish"; "action_delete_poll" = "Delete Poll"; "action_disable" = "Oʻchirish"; @@ -64,6 +64,7 @@ "action_leave" = "Tark etish"; "action_leave_conversation" = "Leave conversation"; "action_leave_room" = "Xonani tark etish"; +"action_load_more" = "Load more"; "action_manage_account" = "Hisobni boshqarish"; "action_manage_devices" = "Qurilmalarni boshqarish"; "action_message" = "Message"; @@ -93,6 +94,7 @@ "action_send_message" = "Xabar yuborish"; "action_share" = "Ulashish"; "action_share_link" = "Havolani ulashing"; +"action_show" = "Show"; "action_sign_in_again" = "Qaytadan kiring"; "action_signout" = "Tizimdan chiqish"; "action_signout_anyway" = "Baribir tizimdan chiqing"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "View in timeline"; "action_view_source" = "Manbani korish"; "action_yes" = "Ha"; -"action.load_more" = "Load more"; -"action_deactivate_account" = "Deactivate account"; "banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; @@ -162,6 +162,7 @@ "common_modern" = "Zamonaviy"; "common_mute" = "Ovozsiz qilish"; "common_no_results" = "Natijalar yoʻq"; +"common_no_room_name" = "No room name"; "common_offline" = "Oflayn"; "common_optic_id_ios" = "Optic ID"; "common_or" = "or"; @@ -170,6 +171,8 @@ "common_permalink" = "Doimiy havola"; "common_permission" = "Ruxsat"; "common_please_wait" = "Please wait…"; +"common_poll_end_confirmation" = "Haqiqatan ham bu soʻrovnomani tugatmoqchimisiz?"; +"common_poll_summary" = "So‘rov:%1$@"; "common_poll_total_votes" = "Jami ovozlar:%1$@"; "common_poll_undisclosed_text" = "Natijalar soʻrovnoma tugagandan soʻng koʻrsatiladi"; "common_privacy_policy" = "Maxfiylik siyosati"; @@ -200,6 +203,7 @@ "common_settings" = "Sozlamalar"; "common_shared_location" = "Joylashuvi ulashildi"; "common_signing_out" = "Signing out"; +"common_something_went_wrong" = "Something went wrong"; "common_starting_chat" = "Chat boshlanmoqda…"; "common_sticker" = "Stiker"; "common_success" = "Muvaffaqiyat"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "Bu xona nima haqida?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Shifrni ochish imkonsiz"; +"common_unable_to_decrypt_no_access" = "You don't have access to this message"; "common_unable_to_invite_message" = "Takliflarni bir yoki bir nechta foydalanuvchiga yuborib bo‘lmadi."; "common_unable_to_invite_title" = "Taklif(lar)ni yuborib bo‘lmadi"; "common_unlock" = "Unlock"; @@ -221,24 +226,21 @@ "common_username" = "Foydalanuvchi nomi"; "common_verification_cancelled" = "Tasdiqlash bekor qilindi"; "common_verification_complete" = "Tasdiqlash yakunlandi"; +"common_verify_device" = "Verify device"; "common_video" = "Video"; "common_voice_message" = "Ovozli xabar"; "common_waiting" = "Kutilmoqda…"; "common_waiting_for_decryption_key" = "Waiting for this message"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "Do not show this again"; "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "Send to"; "common.you" = "You"; -"common_no_room_name" = "No room name"; -"common_poll_end_confirmation" = "Haqiqatan ham bu soʻrovnomani tugatmoqchimisiz?"; -"common_poll_summary" = "So‘rov:%1$@"; -"common_something_went_wrong" = "Something went wrong"; -"common_unable_to_decrypt_no_access" = "You don't have access to this message"; -"common_verify_device" = "Verify device"; "confirm_recovery_key_banner_message" = "Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup."; "confirm_recovery_key_banner_title" = "Enter your recovery key"; "crash_detection_dialog_content" = "%1$@oxirgi marta ishlatilganda qulab tushdi. Biz bilan nosozlik hisobotini baham ko'rmoqchimisiz?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "Ilovaga kameradan foydalanishiga ruxsat berish uchun tizim sozlamalarida ruxsat bering."; "dialog_permission_generic" = "Iltimos, tizim sozlamalarida ruxsat bering."; "dialog_permission_location_description_ios" = "Grant access in Settings -> Location."; @@ -291,7 +293,6 @@ "notification_channel_silent" = "Ovozsiz bildirishnomalar"; "notification_incoming_call" = "Incoming call"; "notification_inline_reply_failed" = "** Yuborilmadi - iltimos, xonani oching"; -"notification_invitation_action_reject" = "Rad etish"; "notification_invite_body" = "Sizni suhbatga taklif qildi"; "notification_invite_body_with_sender" = "%1$@ invited you to chat"; "notification_mentioned_you_body" = "Mentioned you: %1$@"; @@ -335,7 +336,6 @@ "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_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; -"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; "screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "Hisob provayderini o'zgartiring"; "screen_account_provider_form_hint" = "Uy server manzili"; "screen_account_provider_form_notice" = "Qidiruv so'zini yoki domen manzilini kiriting."; "screen_account_provider_form_subtitle" = "Kompaniya, jamoa yoki shaxsiy serverni qidiring."; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "Zaxiralash xabarlar tarixini yo'qotmaslikni ta'minlaydi.%1$@."; "screen_chat_backup_key_backup_title" = "Zaxira"; "screen_chat_backup_recovery_action_change" = "Qayta tiklash kalitini o'zgartiring"; -"screen_chat_backup_recovery_action_confirm" = "Qayta tiklash kalitini kiriting"; "screen_chat_backup_recovery_action_confirm_description" = "Sizning chat zaxirangiz hozirda sinxronlashtirilmagan."; "screen_chat_backup_recovery_action_setup" = "Qayta tiklashni sozlang"; "screen_chat_backup_recovery_action_setup_description" = "Agar barcha qurilmalaringizni yo‘qotib qo‘ysangiz yoki tizimdan chiqqan bo‘lsangiz, shifrlangan xabarlaringizga ruxsat oling%1$@ hamma joyda."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "Natijalarni faqat soʻrov tugagandan keyin koʻrsatish"; "screen_create_poll_anonymous_headline" = "Ovozlarni yashirish"; "screen_create_poll_answer_hint" = "Variant%1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "Oʻzgartirishlaringiz saqlanmaydi"; "screen_create_poll_cancel_confirmation_title_ios" = "So‘rovni bekor qilish"; "screen_create_poll_question_desc" = "Savol yoki mavzu"; "screen_create_poll_question_hint" = "So'rovnoma nima haqida?"; @@ -480,7 +477,7 @@ "screen_edit_profile_updating_details" = "Profil yangilanmoqda…"; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history"; +"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "Guruh suhbatlari"; "screen_notification_settings_invite_for_me_label" = "Invitations"; "screen_notification_settings_mentions_only_disclaimer" = "Your homeserver does not support this option in encrypted rooms, you may not get notified in some rooms."; -"screen_notification_settings_mentions_section_title" = "Eslatmalar"; "screen_notification_settings_mode_all" = "Hammasi"; "screen_notification_settings_mode_mentions" = "Eslatmalar"; "screen_notification_settings_notification_section_title" = "Menga xabar bering"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Kirish…"; "screen_recovery_key_confirm_lost_recovery_key" = "Lost your recovery key?"; "screen_recovery_key_confirm_success" = "Qayta tiklash kaliti tasdiqlandi"; -"screen_recovery_key_confirm_title" = "Qayta tiklash kalitingizni kiriting"; "screen_recovery_key_copied_to_clipboard" = "Copied recovery key"; "screen_recovery_key_generating_key" = "Generating…"; "screen_recovery_key_save_action" = "Qayta tiklash kalitini saqlang"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Yes, reset now"; "screen_reset_encryption_confirmation_alert_subtitle" = "This process is irreversible."; "screen_reset_encryption_confirmation_alert_title" = "Are you sure you want to reset your identity?"; -"screen_reset_encryption_password_placeholder" = "Enter…"; "screen_reset_encryption_password_subtitle" = "Confirm that you want to reset your identity."; "screen_reset_encryption_password_title" = "Enter your account password to continue"; "screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Admins automatically have moderator privileges"; "screen_room_change_role_moderators_title" = "Edit Moderators"; "screen_room_change_role_unsaved_changes_description" = "You have unsaved changes."; -"screen_room_change_role_unsaved_changes_title" = "Save changes?"; "screen_room_details_add_topic_title" = "Mavzu qo'shish"; "screen_room_details_already_a_member" = "Allaqachon a'zo"; "screen_room_details_already_invited" = "Allaqachon taklif qilingan"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "Bu xonaning ovozi yoqilmadi, qayta urinib ko‘ring."; "screen_room_details_notification_mode_custom" = "Maxsus"; "screen_room_details_notification_mode_default" = "Standart"; -"screen_room_details_notification_title" = "Bildirishnomalar"; "screen_room_details_share_room_title" = "Xonani baham ko'ring"; "screen_room_details_title" = "Room info"; "screen_room_details_updating_room" = "Xona yangilanmoqda…"; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "Banning %1$@"; "screen_room_member_list_manage_member_ban" = "Remove and ban member"; "screen_room_member_list_manage_member_remove" = "Remove from room"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "Remove and ban member"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Only remove member"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Remove member and ban from joining in the future?"; "screen_room_member_list_manage_member_unban_action" = "Unban"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "Mark as read"; "screen_roomlist_mark_as_unread" = "Mark as unread"; "screen_roomlist_room_directory_button_title" = "Browse all rooms"; -"screen_server_confirmation_change_server" = "Hisob provayderini o'zgartiring"; "screen_server_confirmation_message_login_element_dot_io" = "Element xodimlari uchun shaxsiy server."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix xavfsiz, markazlashmagan aloqa uchun ochiq tarmoqdir."; "screen_server_confirmation_message_register" = "Bu sizning suhbatlaringiz yashaydigan joy - xuddi siz elektron pochta xabarlaringizni saqlash uchun elektron pochta provayderidan foydalanganingiz kabi."; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "You are about to sign out of your last session. If you sign out now, you will lose access to your encrypted messages."; "screen_signout_key_backup_disabled_title" = "You have turned off backup"; "screen_signout_key_backup_offline_subtitle" = "Your keys were still being backed up when you went offline. Reconnect so that your keys can be backed up before signing out."; -"screen_signout_key_backup_offline_title" = "Your keys are still being backed up"; "screen_signout_key_backup_ongoing_subtitle" = "Please wait for this to complete before signing out."; "screen_signout_key_backup_ongoing_title" = "Your keys are still being backed up"; "screen_signout_recovery_disabled_subtitle" = "You are about to sign out of your last session. If you sign out now, you'll lose access to your encrypted messages."; "screen_signout_recovery_disabled_title" = "Recovery not set up"; "screen_signout_save_recovery_key_subtitle" = "You are about to sign out of your last session. If you sign out now, you might lose access to your encrypted messages."; -"screen_signout_save_recovery_key_title" = "Have you saved your recovery key?"; "screen_start_chat_error_starting_chat" = "Suhbatni boshlashda xatolik yuz berdi"; "screen_view_location_title" = "Joylashuv"; "screen_welcome_bullet_1" = "Qo'ng'iroqlar, so'ro'vlar, qidiruv va boshqalar shu yil oxirida qo'shiladi."; @@ -920,7 +908,6 @@ "test_language_identifier" = "en"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Troubleshoot"; -"troubleshoot_notifications_entry_point_title" = "Troubleshoot notifications"; "troubleshoot_notifications_screen_action" = "Run tests"; "troubleshoot_notifications_screen_action_again" = "Run tests again"; "troubleshoot_notifications_screen_failure" = "Some tests failed. Please check the details."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Ensure that UnifiedPush distributors are available."; "troubleshoot_notifications_test_unified_push_failure" = "No push distributors found."; "troubleshoot_notifications_test_unified_push_title" = "Check UnifiedPush"; +"a11y_poll" = "So'ro'vnoma"; "dialog_title_error" = "Xato"; "dialog_title_success" = "Muvaffaqiyat"; "notification_fallback_content" = "Bildirishnoma"; "notification_invitation_action_join" = "Qo'shilish"; +"notification_invitation_action_reject" = "Reject"; "notification_room_action_mark_as_read" = "Mark as read"; "notification_room_action_quick_reply" = "Tez javob"; +"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_room_mentions_at_room_title" = "Har kim"; +"screen_account_provider_change" = "Hisob provayderini o'zgartiring"; "screen_account_provider_signin_subtitle" = "Bu sizning suhbatlaringiz yashaydigan joy - xuddi siz elektron pochta xabarlaringizni saqlash uchun elektron pochta provayderidan foydalanganingiz kabi."; "screen_account_provider_signup_subtitle" = "Bu sizning suhbatlaringiz yashaydigan joy - xuddi siz elektron pochta xabarlaringizni saqlash uchun elektron pochta provayderidan foydalanganingiz kabi."; "screen_analytics_settings_help_us_improve" = "Muammolarni aniqlashda yordam berish uchun anonim foydalanish maʼlumotlarini baham koʻring."; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "Ulardan kelgan barcha xabarlarni yana koʻrishingiz mumkin boʻladi."; "screen_blocked_users_unblock_alert_title" = "Foydalanuvchini blokdan chiqarish"; "screen_bug_report_rash_logs_alert_title" = "%1$@oxirgi marta ishlatilganda qulab tushdi. Biz bilan nosozlik hisobotini baham ko'rmoqchimisiz?"; +"screen_chat_backup_recovery_action_confirm" = "Enter recovery key"; +"screen_create_poll_cancel_confirmation_content_ios" = "Your changes won’t be saved"; "screen_create_room_add_people_title" = "Odamlarni taklif qiling"; "screen_create_room_room_name_label" = "Xona nomi"; "screen_create_room_title" = "Xonani yaratish"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "So‘rovnomani tahrirlash"; "screen_identity_use_another_device" = "Use another device"; "screen_login_subtitle" = "Matrix xavfsiz, markazlashmagan aloqa uchun ochiq tarmoqdir."; +"screen_notification_settings_mentions_section_title" = "Eslatmalar"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Try again"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Foydalanuvchini bloklash"; +"screen_reset_encryption_password_placeholder" = "Kirish…"; "screen_room_attachment_source_camera_photo" = "Rasmga olmoq"; "screen_room_change_permissions_everyone" = "Har kim"; "screen_room_change_permissions_member_moderation" = "Member moderation"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "Admins"; "screen_room_change_role_section_moderators" = "Moderators"; "screen_room_change_role_section_users" = "Members"; +"screen_room_change_role_unsaved_changes_title" = "Save changes?"; "screen_room_details_invite_people_title" = "Odamlarni taklif qiling"; "screen_room_details_leave_conversation_title" = "Leave conversation"; "screen_room_details_leave_room_title" = "Xonani tark etish"; +"screen_room_details_notification_title" = "Bildirishnomalar"; "screen_room_details_roles_and_permissions" = "Roles and permissions"; "screen_room_details_room_name_label" = "Xona nomi"; "screen_room_details_security_title" = "Xavfsizlik"; "screen_room_details_topic_title" = "Mavzu"; "screen_room_error_failed_processing_media" = "Mediani yuklab bo‘lmadi, qayta urinib ko‘ring."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "Remove and ban member"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Faqat eslatmalar va kalit so'zlar"; "screen_roomlist_filter_people" = "Odamlar"; +"screen_server_confirmation_change_server" = "Hisob provayderini o'zgartiring"; "screen_signout_confirmation_dialog_submit" = "Tizimdan chiqish"; "screen_signout_confirmation_dialog_title" = "Tizimdan chiqish"; +"screen_signout_key_backup_offline_title" = "Your keys are still being backed up"; "screen_signout_preference_item" = "Tizimdan chiqish"; +"screen_signout_save_recovery_key_title" = "Zaxira kalitingizni saqladingizmi?"; +"troubleshoot_notifications_entry_point_title" = "Troubleshoot notifications"; diff --git a/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings b/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings index 7eea56e269..4ffda912d5 100644 --- a/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "暂停"; "a11y_pin_field" = "PIN 字段"; "a11y_play" = "播放"; -"a11y_poll" = "投票"; "a11y_poll_end" = "投票已结束"; "a11y_react_with" = "使用 %1$@ 回应"; "a11y_react_with_other_emojis" = "使用其他表情符号回应"; @@ -41,6 +40,7 @@ "action_create" = "创建"; "action_create_a_room" = "创建房间"; "action_deactivate" = "Deactivate"; +"action_deactivate_account" = "Deactivate account"; "action_decline" = "拒绝"; "action_delete_poll" = "删除投票"; "action_disable" = "停用"; @@ -64,6 +64,7 @@ "action_leave" = "离开"; "action_leave_conversation" = "离开聊天"; "action_leave_room" = "离开房间"; +"action_load_more" = "载入更多"; "action_manage_account" = "管理账户"; "action_manage_devices" = "管理设备"; "action_message" = "发送消息给"; @@ -93,6 +94,7 @@ "action_send_message" = "发送消息"; "action_share" = "分享"; "action_share_link" = "分享链接"; +"action_show" = "Show"; "action_sign_in_again" = "再次登录"; "action_signout" = "登出"; "action_signout_anyway" = "仍然登出"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "在时间轴中查看"; "action_view_source" = "查看源码"; "action_yes" = "是"; -"action.load_more" = "载入更多"; -"action_deactivate_account" = "Deactivate account"; "banner_migrate_to_native_sliding_sync_action" = "登出并升级"; "banner_migrate_to_native_sliding_sync_description" = "您的服务器现在支持更快的新协议。现在登出并重新登录以进行升级。现在这样做可以帮助您避免在以后删除旧协议时被强制登出。"; "banner_migrate_to_native_sliding_sync_force_logout_title" = "您的服务器不再支持旧协议。请登出并重新登录以继续使用此应用。"; @@ -162,6 +162,7 @@ "common_modern" = "现代"; "common_mute" = "静音"; "common_no_results" = "没有结果"; +"common_no_room_name" = "无房间名"; "common_offline" = "离线"; "common_optic_id_ios" = "光学 ID"; "common_or" = "或"; @@ -170,6 +171,8 @@ "common_permalink" = "固定链接"; "common_permission" = "权限"; "common_please_wait" = "请稍候……"; +"common_poll_end_confirmation" = "确定要结束这个投票吗?"; +"common_poll_summary" = "投票:%1$@"; "common_poll_total_votes" = "总票数: %1$@"; "common_poll_undisclosed_text" = "结果将在投票结束后显示"; "common_privacy_policy" = "隐私政策"; @@ -200,6 +203,7 @@ "common_settings" = "设置"; "common_shared_location" = "共享位置"; "common_signing_out" = "正在登出"; +"common_something_went_wrong" = "发生了一些错误"; "common_starting_chat" = "开始聊天..."; "common_sticker" = "贴纸"; "common_success" = "成功"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "这个房间是关于什么的?"; "common_touch_id_ios" = "触控 ID"; "common_unable_to_decrypt" = "无法解密"; +"common_unable_to_decrypt_no_access" = "无权访问此消息"; "common_unable_to_invite_message" = "无法向一个或多个用户发送邀请。"; "common_unable_to_invite_title" = "无法发送邀请"; "common_unlock" = "解锁"; @@ -221,24 +226,21 @@ "common_username" = "用户名"; "common_verification_cancelled" = "验证已取消"; "common_verification_complete" = "验证完成"; +"common_verify_device" = "验证设备"; "common_video" = "视频"; "common_voice_message" = "语音消息"; "common_waiting" = "等待..."; "common_waiting_for_decryption_key" = "正在等待解密密钥"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "不再显示此内容"; "common.open_source_licenses" = "开源许可证"; "common.pinned" = "Pinned"; "common.send_to" = "发送至"; "common.you" = "You"; -"common_no_room_name" = "无房间名"; -"common_poll_end_confirmation" = "确定要结束这个投票吗?"; -"common_poll_summary" = "投票:%1$@"; -"common_something_went_wrong" = "发生了一些错误"; -"common_unable_to_decrypt_no_access" = "无权访问此消息"; -"common_verify_device" = "验证设备"; "confirm_recovery_key_banner_message" = "聊天备份目前不同步,需要输入恢复密钥才能访问聊天备份。"; "confirm_recovery_key_banner_title" = "输入恢复密钥"; "crash_detection_dialog_content" = "%1$@ 上次使用时崩溃了。想和我们分享崩溃报告吗?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "为了让应用程序使用相机,请在系统设置中授予权限。"; "dialog_permission_generic" = "请在系统设置中授予权限。"; "dialog_permission_location_description_ios" = "在设置->位置中授予访问权限。"; @@ -291,7 +293,6 @@ "notification_channel_silent" = "静默通知"; "notification_incoming_call" = "来电"; "notification_inline_reply_failed" = "** 无法发送——请打开房间"; -"notification_invitation_action_reject" = "拒绝"; "notification_invite_body" = "邀请您聊天"; "notification_invite_body_with_sender" = "%1$@ invited you to chat"; "notification_mentioned_you_body" = "提到了你:%1$@"; @@ -335,7 +336,6 @@ "screen_advanced_settings_element_call_base_url_validation_error" = "URL 无效,请确保包含协议(http/https)和正确的地址。"; "screen_pinned_timeline_empty_state_description" = "按下消息并选择 “%1$@” 将其包含在此处。"; "screen_pinned_timeline_empty_state_headline" = "固定重要消息,以便轻松发现它们"; -"screen_pinned_timeline_screen_title_empty" = "置顶消息"; "screen_reset_encryption_password_error" = "发生未知错误。请检查您的帐户密码是否正确,然后重试。"; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; "screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "消息未发送,因为%1$@尚未验证所有设备。"; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "更改账户提供者"; "screen_account_provider_form_hint" = "服务器地址"; "screen_account_provider_form_notice" = "输入搜索词或域名地址。"; "screen_account_provider_form_subtitle" = "搜索公司、社区或私人服务器。"; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "备份可确保你不会丢失消息历史记录。%1$@。"; "screen_chat_backup_key_backup_title" = "备份"; "screen_chat_backup_recovery_action_change" = "更改恢复密钥"; -"screen_chat_backup_recovery_action_confirm" = "输入恢复密钥"; "screen_chat_backup_recovery_action_confirm_description" = "您的聊天备份当前不同步。"; "screen_chat_backup_recovery_action_setup" = "设置恢复密钥"; "screen_chat_backup_recovery_action_setup_description" = "在丢失或从 %1$@ 登出所有设备的情况下访问加密消息。"; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "仅在投票结束后显示结果"; "screen_create_poll_anonymous_headline" = "隐藏投票"; "screen_create_poll_answer_hint" = "选项 %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "您的更改不会保存"; "screen_create_poll_cancel_confirmation_title_ios" = "取消投票"; "screen_create_poll_question_desc" = "问题或话题"; "screen_create_poll_question_hint" = "投票的内容是什么?"; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "群聊"; "screen_notification_settings_invite_for_me_label" = "邀请"; "screen_notification_settings_mentions_only_disclaimer" = "您的服务器在加密房间中不支持此选项,因此在某些房间您可能无法收到通知。"; -"screen_notification_settings_mentions_section_title" = "提及"; "screen_notification_settings_mode_all" = "全部"; "screen_notification_settings_mode_mentions" = "提及"; "screen_notification_settings_notification_section_title" = "请通知我:"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "输入……"; "screen_recovery_key_confirm_lost_recovery_key" = "丢失了恢复密钥?"; "screen_recovery_key_confirm_success" = "恢复密钥已确认"; -"screen_recovery_key_confirm_title" = "输入您的恢复密钥"; "screen_recovery_key_copied_to_clipboard" = "恢复密钥已复制"; "screen_recovery_key_generating_key" = "正在生成……"; "screen_recovery_key_save_action" = "保存恢复密钥"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "是的,立即重置"; "screen_reset_encryption_confirmation_alert_subtitle" = "此过程不可逆。"; "screen_reset_encryption_confirmation_alert_title" = "您确定要重置加密吗?"; -"screen_reset_encryption_password_placeholder" = "输入..."; "screen_reset_encryption_password_subtitle" = "确认您要重置加密。"; "screen_reset_encryption_password_title" = "输入您的账户密码以继续"; "screen_reset_identity_confirmation_subtitle" = "您将要转到您的%1$@帐户来重置您的身份信息。之后,您将被带回该应用。"; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "管理员自动拥有协管员权限"; "screen_room_change_role_moderators_title" = "编辑协管员"; "screen_room_change_role_unsaved_changes_description" = "您有未保存的更改。"; -"screen_room_change_role_unsaved_changes_title" = "保存更改?"; "screen_room_details_add_topic_title" = "添加主题"; "screen_room_details_already_a_member" = "已经是成员"; "screen_room_details_already_invited" = "已邀请"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "无法取消此房间的静音,请重试。"; "screen_room_details_notification_mode_custom" = "自定义"; "screen_room_details_notification_mode_default" = "默认"; -"screen_room_details_notification_title" = "通知"; "screen_room_details_share_room_title" = "分享房间"; "screen_room_details_title" = "聊天室信息"; "screen_room_details_updating_room" = "正在更新房间……"; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "封禁 %1$@"; "screen_room_member_list_manage_member_ban" = "移除并封禁成员"; "screen_room_member_list_manage_member_remove" = "从房间移除"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "移除并封禁成员"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "仅移除成员"; "screen_room_member_list_manage_member_remove_confirmation_title" = "删除成员并禁止重新加入?"; "screen_room_member_list_manage_member_unban_action" = "取消封禁"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "标记为已读"; "screen_roomlist_mark_as_unread" = "标记为未读"; "screen_roomlist_room_directory_button_title" = "浏览所有房间"; -"screen_server_confirmation_change_server" = "更改账户提供者"; "screen_server_confirmation_message_login_element_dot_io" = "专为 Element 员工提供的私人服务器。"; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix 是一个用于安全、去中心化通信的开放网络。"; "screen_server_confirmation_message_register" = "这是您的对话将进行的地方,就像您使用电子邮件提供商来保存电子邮件一样。"; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "即将登出最后一个会话。如果现在登出,将无法访问加密的消息。"; "screen_signout_key_backup_disabled_title" = "您已关闭备份"; "screen_signout_key_backup_offline_subtitle" = "当你离线时,密钥仍在备份中。重新连接以便在登出之前备份密钥。"; -"screen_signout_key_backup_offline_title" = "您的密钥仍在备份中"; "screen_signout_key_backup_ongoing_subtitle" = "请等待此操作完成后再登出。"; "screen_signout_key_backup_ongoing_title" = "您的密钥仍在备份中"; "screen_signout_recovery_disabled_subtitle" = "即将登出最后一个会话。如果现在登出,将无法访问加密的消息。"; "screen_signout_recovery_disabled_title" = "未设置恢复"; "screen_signout_save_recovery_key_subtitle" = "即将登出最后一个会话。如果现在登出,将无法访问加密的消息。"; -"screen_signout_save_recovery_key_title" = "您保存了恢复密钥吗?"; "screen_start_chat_error_starting_chat" = "在开始聊天时发生了错误"; "screen_view_location_title" = "位置"; "screen_welcome_bullet_1" = "今年晚些时候将增加通话、投票、搜索等功能。"; @@ -920,7 +908,6 @@ "test_language_identifier" = "zh-Hans"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "排查问题"; -"troubleshoot_notifications_entry_point_title" = "排查通知问题"; "troubleshoot_notifications_screen_action" = "运行测试"; "troubleshoot_notifications_screen_action_again" = "再次运行测试"; "troubleshoot_notifications_screen_failure" = "一些测试失败了。请查看详情。"; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "确保 UnifiedPush distributor 可用。"; "troubleshoot_notifications_test_unified_push_failure" = "未找到推送 distributor。"; "troubleshoot_notifications_test_unified_push_title" = "检查 UnifiedPush"; +"a11y_poll" = "投票"; "dialog_title_error" = "错误"; "dialog_title_success" = "成功"; "notification_fallback_content" = "通知"; "notification_invitation_action_join" = "加入"; +"notification_invitation_action_reject" = "拒绝"; "notification_room_action_mark_as_read" = "标记为已读"; "notification_room_action_quick_reply" = "快速回复"; +"screen_pinned_timeline_screen_title_empty" = "置顶消息"; "screen_room_mentions_at_room_title" = "所有人"; +"screen_account_provider_change" = "更改账户提供者"; "screen_account_provider_signin_subtitle" = "这是您的对话将进行的地方,就像您使用电子邮件提供商来保存电子邮件一样。"; "screen_account_provider_signup_subtitle" = "这是您的对话将进行的地方,就像您使用电子邮件提供商来保存电子邮件一样。"; "screen_analytics_settings_help_us_improve" = "共享匿名使用数据以帮助我们排查问题。"; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "可以重新接收他们的消息。"; "screen_blocked_users_unblock_alert_title" = "解封用户"; "screen_bug_report_rash_logs_alert_title" = "%1$@ 上次使用时崩溃了。想和我们分享崩溃报告吗?"; +"screen_chat_backup_recovery_action_confirm" = "输入恢复密钥"; +"screen_create_poll_cancel_confirmation_content_ios" = "更改不会保存"; "screen_create_room_add_people_title" = "邀请朋友"; "screen_create_room_room_name_label" = "房间名称"; "screen_create_room_title" = "创建房间"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "编辑投票"; "screen_identity_use_another_device" = "使用其他设备"; "screen_login_subtitle" = "Matrix 是一个用于安全、去中心化通信的开放网络。"; +"screen_notification_settings_mentions_section_title" = "提及"; "screen_qr_code_login_invalid_scan_state_retry_button" = "再试一次"; +"screen_recovery_key_confirm_title" = "输入恢复密钥"; "screen_report_content_block_user" = "封禁用户"; +"screen_reset_encryption_password_placeholder" = "输入……"; "screen_room_attachment_source_camera_photo" = "拍摄照片"; "screen_room_change_permissions_everyone" = "所有人"; "screen_room_change_permissions_member_moderation" = "成员权限"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "管理员"; "screen_room_change_role_section_moderators" = "协管员"; "screen_room_change_role_section_users" = "成员"; +"screen_room_change_role_unsaved_changes_title" = "保存更改?"; "screen_room_details_invite_people_title" = "邀请朋友"; "screen_room_details_leave_conversation_title" = "离开聊天"; "screen_room_details_leave_room_title" = "离开房间"; +"screen_room_details_notification_title" = "通知"; "screen_room_details_roles_and_permissions" = "角色与权限"; "screen_room_details_room_name_label" = "房间名称"; "screen_room_details_security_title" = "安全"; "screen_room_details_topic_title" = "话题"; "screen_room_error_failed_processing_media" = "处理要上传的媒体失败,请重试。"; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "移除并封禁成员"; "screen_room_notification_settings_mode_mentions_and_keywords" = "仅限提及和关键词"; "screen_roomlist_filter_people" = "用户"; +"screen_server_confirmation_change_server" = "更改账户提供者"; "screen_signout_confirmation_dialog_submit" = "登出"; "screen_signout_confirmation_dialog_title" = "登出"; +"screen_signout_key_backup_offline_title" = "您的密钥仍在备份中"; "screen_signout_preference_item" = "登出"; +"screen_signout_save_recovery_key_title" = "您保存了恢复密钥吗?"; +"troubleshoot_notifications_entry_point_title" = "排查通知问题"; diff --git a/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings b/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings index 886f7008ac..5aaedaacaf 100644 --- a/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings @@ -8,7 +8,6 @@ "a11y_pause" = "暫停"; "a11y_pin_field" = "PIN 碼欄位"; "a11y_play" = "播放"; -"a11y_poll" = "投票"; "a11y_poll_end" = "投票已結束"; "a11y_react_with" = "使用 %1$@ 回應"; "a11y_react_with_other_emojis" = "用其他表情符號回應"; @@ -41,6 +40,7 @@ "action_create" = "建立"; "action_create_a_room" = "建立聊天室"; "action_deactivate" = "Deactivate"; +"action_deactivate_account" = "Deactivate account"; "action_decline" = "拒絕"; "action_delete_poll" = "刪除投票"; "action_disable" = "停用"; @@ -64,6 +64,7 @@ "action_leave" = "離開"; "action_leave_conversation" = "離開對話"; "action_leave_room" = "離開聊天室"; +"action_load_more" = "載入更多"; "action_manage_account" = "管理帳號"; "action_manage_devices" = "管理裝置"; "action_message" = "聊天"; @@ -93,6 +94,7 @@ "action_send_message" = "傳送訊息"; "action_share" = "分享"; "action_share_link" = "分享連結"; +"action_show" = "Show"; "action_sign_in_again" = "再登入一次"; "action_signout" = "登出"; "action_signout_anyway" = "直接登出"; @@ -108,8 +110,6 @@ "action_view_in_timeline" = "View in timeline"; "action_view_source" = "檢視原始碼"; "action_yes" = "是"; -"action.load_more" = "載入更多"; -"action_deactivate_account" = "Deactivate account"; "banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; @@ -162,6 +162,7 @@ "common_modern" = "現代"; "common_mute" = "關閉通知"; "common_no_results" = "查無結果"; +"common_no_room_name" = "無聊天室名稱"; "common_offline" = "離線"; "common_optic_id_ios" = "Optic ID"; "common_or" = "或"; @@ -170,6 +171,8 @@ "common_permalink" = "永久連結"; "common_permission" = "權限"; "common_please_wait" = "請稍等..."; +"common_poll_end_confirmation" = "您確定要結束這項投票嗎?"; +"common_poll_summary" = "投票:%1$@"; "common_poll_total_votes" = "總票數:%1$@"; "common_poll_undisclosed_text" = "結果將在投票結束後公佈"; "common_privacy_policy" = "隱私權政策"; @@ -200,6 +203,7 @@ "common_settings" = "設定"; "common_shared_location" = "位置分享"; "common_signing_out" = "正在登出"; +"common_something_went_wrong" = "有錯誤發生"; "common_starting_chat" = "開始聊天..."; "common_sticker" = "貼圖"; "common_success" = "成功"; @@ -213,6 +217,7 @@ "common_topic_placeholder" = "What is this room about?"; "common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "無法解密"; +"common_unable_to_decrypt_no_access" = "您無法存取此則訊息"; "common_unable_to_invite_message" = "無法發送邀請給一或多個使用者。"; "common_unable_to_invite_title" = "無法發送邀請"; "common_unlock" = "解鎖"; @@ -221,24 +226,21 @@ "common_username" = "使用者名稱"; "common_verification_cancelled" = "驗證已取消"; "common_verification_complete" = "驗證完成"; +"common_verify_device" = "驗證裝置"; "common_video" = "影片"; "common_voice_message" = "語音訊息"; "common_waiting" = "等待中..."; "common_waiting_for_decryption_key" = "等待此則訊息"; +"common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "不再顯示"; "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; "common.send_to" = "傳送給"; "common.you" = "You"; -"common_no_room_name" = "無聊天室名稱"; -"common_poll_end_confirmation" = "您確定要結束這項投票嗎?"; -"common_poll_summary" = "投票:%1$@"; -"common_something_went_wrong" = "有錯誤發生"; -"common_unable_to_decrypt_no_access" = "您無法存取此則訊息"; -"common_verify_device" = "驗證裝置"; "confirm_recovery_key_banner_message" = "Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup."; "confirm_recovery_key_banner_title" = "輸入您的復原金鑰"; "crash_detection_dialog_content" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; +"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "dialog_permission_camera" = "為了讓應用程式使用相機,請到系統設定中開啟權限。"; "dialog_permission_generic" = "請到系統設定中開啟權限。"; "dialog_permission_location_description_ios" = "在「設定」->「位置」中開啟權限。"; @@ -291,7 +293,6 @@ "notification_channel_silent" = "無聲通知"; "notification_incoming_call" = "Incoming call"; "notification_inline_reply_failed" = "** 無法傳送,請開啟聊天室"; -"notification_invitation_action_reject" = "拒絕"; "notification_invite_body" = "邀請您聊天"; "notification_invite_body_with_sender" = "%1$@ invited you to chat"; "notification_mentioned_you_body" = "提及您:%1$@"; @@ -335,7 +336,6 @@ "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_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; -"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; "screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; @@ -354,7 +354,6 @@ "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; -"screen_account_provider_change" = "更改帳號提供者"; "screen_account_provider_form_hint" = "家伺服器位址"; "screen_account_provider_form_notice" = "輸入關鍵字或網域名稱。"; "screen_account_provider_form_subtitle" = "搜尋公司、社群、私有伺服器。"; @@ -432,7 +431,6 @@ "screen_chat_backup_key_backup_description" = "備份可確保您不會遺失歷史訊息。%1$@。"; "screen_chat_backup_key_backup_title" = "備份"; "screen_chat_backup_recovery_action_change" = "變更復原金鑰"; -"screen_chat_backup_recovery_action_confirm" = "輸入復原金鑰"; "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."; @@ -448,7 +446,6 @@ "screen_create_poll_anonymous_desc" = "只在投票結束後顯示結果"; "screen_create_poll_anonymous_headline" = "隱藏票數"; "screen_create_poll_answer_hint" = "選項 %1$d"; -"screen_create_poll_cancel_confirmation_content_ios" = "您的變更不會保留"; "screen_create_poll_cancel_confirmation_title_ios" = "捨棄投票"; "screen_create_poll_question_desc" = "問題或主題"; "screen_create_poll_question_hint" = "投什麼?"; @@ -480,7 +477,7 @@ "screen_edit_profile_updating_details" = "正在更新個人檔案..."; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history"; +"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -545,7 +542,6 @@ "screen_notification_settings_group_chats" = "群組聊天"; "screen_notification_settings_invite_for_me_label" = "邀請"; "screen_notification_settings_mentions_only_disclaimer" = "Your homeserver does not support this option in encrypted rooms, you may not get notified in some rooms."; -"screen_notification_settings_mentions_section_title" = "提及"; "screen_notification_settings_mode_all" = "All"; "screen_notification_settings_mode_mentions" = "提及"; "screen_notification_settings_notification_section_title" = "Notify me for"; @@ -617,7 +613,6 @@ "screen_recovery_key_confirm_key_placeholder" = "Enter…"; "screen_recovery_key_confirm_lost_recovery_key" = "Lost your recovery key?"; "screen_recovery_key_confirm_success" = "Recovery key confirmed"; -"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_recovery_key_copied_to_clipboard" = "Copied recovery key"; "screen_recovery_key_generating_key" = "Generating…"; "screen_recovery_key_save_action" = "Save recovery key"; @@ -637,7 +632,6 @@ "screen_reset_encryption_confirmation_alert_action" = "Yes, reset now"; "screen_reset_encryption_confirmation_alert_subtitle" = "This process is irreversible."; "screen_reset_encryption_confirmation_alert_title" = "Are you sure you want to reset your identity?"; -"screen_reset_encryption_password_placeholder" = "Enter…"; "screen_reset_encryption_password_subtitle" = "Confirm that you want to reset your identity."; "screen_reset_encryption_password_title" = "Enter your account password to continue"; "screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; @@ -670,7 +664,6 @@ "screen_room_change_role_moderators_admin_section_footer" = "Admins automatically have moderator privileges"; "screen_room_change_role_moderators_title" = "編輯版主"; "screen_room_change_role_unsaved_changes_description" = "您有尚未儲存的變更"; -"screen_room_change_role_unsaved_changes_title" = "是否儲存變更?"; "screen_room_details_add_topic_title" = "新增主題"; "screen_room_details_already_a_member" = "已是成員"; "screen_room_details_already_invited" = "已邀請"; @@ -687,7 +680,6 @@ "screen_room_details_error_unmuting" = "無法開啟聊天室通知,請再試一次。"; "screen_room_details_notification_mode_custom" = "自訂"; "screen_room_details_notification_mode_default" = "預設"; -"screen_room_details_notification_title" = "通知"; "screen_room_details_share_room_title" = "分享聊天室"; "screen_room_details_title" = "聊天室資訊"; "screen_room_details_updating_room" = "正在更新聊天室..."; @@ -712,7 +704,6 @@ "screen_room_member_list_banning_user" = "正在將 %1$@ 加入黑名單"; "screen_room_member_list_manage_member_ban" = "踢出並加入黑名單"; "screen_room_member_list_manage_member_remove" = "踢出聊天室"; -"screen_room_member_list_manage_member_remove_confirmation_ban" = "踢出並加入黑名單"; "screen_room_member_list_manage_member_remove_confirmation_kick" = "Only remove member"; "screen_room_member_list_manage_member_remove_confirmation_title" = "Remove member and ban from joining in the future?"; "screen_room_member_list_manage_member_unban_action" = "解除黑名單"; @@ -791,7 +782,6 @@ "screen_roomlist_mark_as_read" = "標為已讀"; "screen_roomlist_mark_as_unread" = "標為未讀"; "screen_roomlist_room_directory_button_title" = "Browse all rooms"; -"screen_server_confirmation_change_server" = "更改帳號提供者"; "screen_server_confirmation_message_login_element_dot_io" = "A private server for Element employees."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix 是一個開放網路,為了安全且去中心化的通訊而生。"; "screen_server_confirmation_message_register" = "您的所有對話將保存於此,就如同您的電子郵件供應商會保存您的電子郵件一樣。"; @@ -831,13 +821,11 @@ "screen_signout_key_backup_disabled_subtitle" = "You are about to sign out of your last session. If you sign out now, you will lose access to your encrypted messages."; "screen_signout_key_backup_disabled_title" = "You have turned off backup"; "screen_signout_key_backup_offline_subtitle" = "Your keys were still being backed up when you went offline. Reconnect so that your keys can be backed up before signing out."; -"screen_signout_key_backup_offline_title" = "Your keys are still being backed up"; "screen_signout_key_backup_ongoing_subtitle" = "Please wait for this to complete before signing out."; "screen_signout_key_backup_ongoing_title" = "Your keys are still being backed up"; "screen_signout_recovery_disabled_subtitle" = "You are about to sign out of your last session. If you sign out now, you'll lose access to your encrypted messages."; "screen_signout_recovery_disabled_title" = "Recovery not set up"; "screen_signout_save_recovery_key_subtitle" = "You are about to sign out of your last session. If you sign out now, you might lose access to your encrypted messages."; -"screen_signout_save_recovery_key_title" = "Have you saved your recovery key?"; "screen_start_chat_error_starting_chat" = "An error occurred when trying to start a chat"; "screen_view_location_title" = "位置"; "screen_welcome_bullet_1" = "通話、投票、搜尋等更多功能將在今年登場。"; @@ -920,7 +908,6 @@ "test_language_identifier" = "zh-tw"; "test_untranslated_default_language_identifier" = "en"; "troubleshoot_notifications_entry_point_section" = "Troubleshoot"; -"troubleshoot_notifications_entry_point_title" = "Troubleshoot notifications"; "troubleshoot_notifications_screen_action" = "Run tests"; "troubleshoot_notifications_screen_action_again" = "Run tests again"; "troubleshoot_notifications_screen_failure" = "Some tests failed. Please check the details."; @@ -962,13 +949,17 @@ "troubleshoot_notifications_test_unified_push_description" = "Ensure that UnifiedPush distributors are available."; "troubleshoot_notifications_test_unified_push_failure" = "No push distributors found."; "troubleshoot_notifications_test_unified_push_title" = "Check UnifiedPush"; +"a11y_poll" = "投票"; "dialog_title_error" = "錯誤"; "dialog_title_success" = "成功"; "notification_fallback_content" = "通知"; "notification_invitation_action_join" = "加入"; +"notification_invitation_action_reject" = "拒絕"; "notification_room_action_mark_as_read" = "標為已讀"; "notification_room_action_quick_reply" = "快速回覆"; +"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; "screen_room_mentions_at_room_title" = "所有人"; +"screen_account_provider_change" = "更改帳號提供者"; "screen_account_provider_signin_subtitle" = "您的所有對話將保存於此,就如同您的電子郵件供應商會保存您的電子郵件一樣。"; "screen_account_provider_signup_subtitle" = "您的所有對話將保存於此,就如同您的電子郵件供應商會保存您的電子郵件一樣。"; "screen_analytics_settings_help_us_improve" = "提供匿名的使用數據以協助我們釐清問題。"; @@ -978,6 +969,8 @@ "screen_blocked_users_unblock_alert_description" = "您將無法看到任何來自他們的訊息。"; "screen_blocked_users_unblock_alert_title" = "解除封鎖使用者"; "screen_bug_report_rash_logs_alert_title" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; +"screen_chat_backup_recovery_action_confirm" = "Enter recovery key"; +"screen_create_poll_cancel_confirmation_content_ios" = "您的變更不會儲存"; "screen_create_room_add_people_title" = "邀請夥伴"; "screen_create_room_room_name_label" = "聊天室名稱"; "screen_create_room_title" = "建立聊天室"; @@ -991,8 +984,11 @@ "screen_edit_poll_title" = "編輯投票"; "screen_identity_use_another_device" = "使用另一部裝置"; "screen_login_subtitle" = "Matrix 是一個開放網路,為了安全且去中心化的通訊而生。"; +"screen_notification_settings_mentions_section_title" = "提及"; "screen_qr_code_login_invalid_scan_state_retry_button" = "再試一次"; +"screen_recovery_key_confirm_title" = "輸入您的復原金鑰"; "screen_report_content_block_user" = "封鎖使用者"; +"screen_reset_encryption_password_placeholder" = "Enter…"; "screen_room_attachment_source_camera_photo" = "拍照"; "screen_room_change_permissions_everyone" = "所有人"; "screen_room_change_permissions_member_moderation" = "成員管理"; @@ -1001,16 +997,23 @@ "screen_room_change_role_section_administrators" = "管理員"; "screen_room_change_role_section_moderators" = "版主"; "screen_room_change_role_section_users" = "成員"; +"screen_room_change_role_unsaved_changes_title" = "是否儲存變更?"; "screen_room_details_invite_people_title" = "邀請夥伴"; "screen_room_details_leave_conversation_title" = "離開對話"; "screen_room_details_leave_room_title" = "離開聊天室"; +"screen_room_details_notification_title" = "通知"; "screen_room_details_roles_and_permissions" = "身份與權限"; "screen_room_details_room_name_label" = "聊天室名稱"; "screen_room_details_security_title" = "安全性"; "screen_room_details_topic_title" = "主題"; "screen_room_error_failed_processing_media" = "Failed processing media to upload, please try again."; +"screen_room_member_list_manage_member_remove_confirmation_ban" = "踢出並加入黑名單"; "screen_room_notification_settings_mode_mentions_and_keywords" = "僅限提及與關鍵字"; "screen_roomlist_filter_people" = "夥伴"; +"screen_server_confirmation_change_server" = "更改帳號提供者"; "screen_signout_confirmation_dialog_submit" = "登出"; "screen_signout_confirmation_dialog_title" = "登出"; +"screen_signout_key_backup_offline_title" = "Your keys are still being backed up"; "screen_signout_preference_item" = "登出"; +"screen_signout_save_recovery_key_title" = "Have you saved your recovery key?"; +"troubleshoot_notifications_entry_point_title" = "Troubleshoot notifications"; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 55bb4b85db..36f6866c76 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -520,6 +520,10 @@ internal enum L10n { internal static func crashDetectionDialogContent(_ p1: Any) -> String { return L10n.tr("Localizable", "crash_detection_dialog_content", String(describing: p1)) } + /// %1$@'s identity appears to have changed. %2$@ + internal static func cryptoIdentityChangePinViolation(_ p1: Any, _ p2: Any) -> String { + return L10n.tr("Localizable", "crypto_identity_change_pin_violation", String(describing: p1), String(describing: p2)) + } /// In order to let the application use the camera, please grant the permission in the system settings. internal static var dialogPermissionCamera: String { return L10n.tr("Localizable", "dialog_permission_camera") } /// Please grant the permission in the system settings. @@ -1119,7 +1123,7 @@ internal enum L10n { internal static var screenEncryptionResetActionContinueReset: String { return L10n.tr("Localizable", "screen_encryption_reset_action_continue_reset") } /// Your account details, contacts, preferences, and chat list will be kept internal static var screenEncryptionResetBullet1: String { return L10n.tr("Localizable", "screen_encryption_reset_bullet_1") } - /// You will lose your existing message history + /// You will lose your existing message history unless it is stored on another device internal static var screenEncryptionResetBullet2: String { return L10n.tr("Localizable", "screen_encryption_reset_bullet_2") } /// You will need to verify all your existing devices and contacts again internal static var screenEncryptionResetBullet3: String { return L10n.tr("Localizable", "screen_encryption_reset_bullet_3") } @@ -2410,6 +2414,8 @@ internal enum L10n { } internal enum Common { + /// Copied to clipboard + internal static var copiedToClipboard: String { return L10n.tr("Localizable", "common.copied_to_clipboard") } /// Do not show this again internal static var doNotShowThisAgain: String { return L10n.tr("Localizable", "common.do_not_show_this_again") } /// Open source licenses diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-en-GB.1.png index 5d8e2096cc..e72f4cff6b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1abe1380fff11977438738ea9e184efe8a6c2d3a4a4eab0b89caef7d5384cf14 -size 153800 +oid sha256:d7b9f1160cb8fd978c7402ead8460e56939c5ab4ee7b8285131b00307426183e +size 159516 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-pseudo.1.png index 8791a6dc30..5998a09d05 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04be415de88b73c1cf1757bdad7f8e7ab5b366b2c669fc9af00e5dd0fde7c519 -size 206442 +oid sha256:625e6b9fccf62cbe1c71aa454f8cd66742f881c96bd661f8418173e7933d5af0 +size 218034 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-en-GB.1.png index e967ef99ee..b6243f868c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c158d6914bbd77b6a473a3042ad9f7ca5be135afc83277542962532311b77023 -size 104899 +oid sha256:1068d4904a804b49209262a271bfa42ee77e69b252af4ccf0cd400e5a91cf5f5 +size 108725 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-pseudo.1.png index 6a82c27e1a..f2062a2523 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c6738f263f313d058b3af80f5d6dd6b752500fe56e3650eda820ebcfb183798a -size 157963 +oid sha256:50b378ce2f752940b0ea0ec64c1965a6e1bc73a9b234505abaec993aea3634af +size 168764 From 794d0eead12883d4fdb8c349fa80eea2d0fd7fd2 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Tue, 1 Oct 2024 08:41:15 +0300 Subject: [PATCH 034/114] Fixes element-hq/element-meta/issues/2525 - Display a warning when a user's pinned identity changes --- ElementX.xcodeproj/project.pbxproj | 6 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- .../Sources/Application/AppSettings.swift | 2 + .../RoomFlowCoordinator.swift | 3 +- .../Mocks/Generated/GeneratedMocks.swift | 76 +++++ .../Mocks/Generated/SDKGeneratedMocks.swift | 287 +++++++++++++----- .../Sources/Mocks/JoinedRoomProxyMock.swift | 1 + .../Sources/Mocks/RoomMemberProxyMock.swift | 5 + .../RoomScreen/RoomScreenCoordinator.swift | 11 +- .../Screens/RoomScreen/RoomScreenModels.swift | 11 + .../RoomScreen/RoomScreenViewModel.swift | 92 +++++- .../Screens/RoomScreen/View/RoomScreen.swift | 35 ++- .../View/RoomScreenFooterView.swift | 78 +++++ .../Sources/Services/Client/ClientProxy.swift | 16 + .../Services/Client/ClientProxyProtocol.swift | 2 + .../ElementCall/ElementCallService.swift | 4 +- .../Services/Room/JoinedRoomProxy.swift | 31 ++ .../Room/RoomMember/RoomMemberProxy.swift | 12 + .../RoomMember/RoomMemberProxyProtocol.swift | 5 + .../Services/Room/RoomProxyProtocol.swift | 4 +- .../EncryptionAuthenticity.swift | 12 +- .../UITests/UITestsAppCoordinator.swift | 39 ++- .../Sources/GeneratedPreviewTests.swift | 6 + ...test_roomScreenFooterView-iPad-en-GB.1.png | 3 + ...est_roomScreenFooterView-iPad-pseudo.1.png | 3 + ...roomScreenFooterView-iPhone-16-en-GB.1.png | 3 + ...oomScreenFooterView-iPhone-16-pseudo.1.png | 3 + UnitTests/Sources/LoggingTests.swift | 29 +- .../Sources/RoomScreenViewModelTests.swift | 24 +- project.yml | 2 +- 30 files changed, 678 insertions(+), 131 deletions(-) create mode 100644 ElementX/Sources/Screens/RoomScreen/View/RoomScreenFooterView.swift create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.1.png diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index d0a5dfe789..38c2216c80 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -1035,6 +1035,7 @@ E82E13CC3EB923CCB8F8273C /* TimelineProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9E543072DE58E751F028998 /* TimelineProxy.swift */; }; E84ADFE9696936C18C2424B5 /* SecureBackupScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A00BB9CD12CF6AC98D5485 /* SecureBackupScreen.swift */; }; E89536FC8C0E4B79E9842A78 /* RoomTimelineControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C0197EAE9D45A662B8847B6 /* RoomTimelineControllerProtocol.swift */; }; + E8C65C19F7C40EE545172DD6 /* RoomScreenFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4137900E28201C314C835C11 /* RoomScreenFooterView.swift */; }; E9347F56CF0683208F4D9249 /* RoomNotificationSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81A9B5225D0881CEFA2CF7C9 /* RoomNotificationSettingsScreenViewModel.swift */; }; E9560744F7B0292E20ECE5F2 /* RoomDetailsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63E8A1E8EE094F570573B6E8 /* RoomDetailsScreenViewModelProtocol.swift */; }; E96005321849DBD7C72A28F2 /* UITestsAppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C208DA43CE25D13E670F40 /* UITestsAppCoordinator.swift */; }; @@ -1488,6 +1489,7 @@ 406C90AF8C3E98DF5D4E5430 /* ElementCallServiceConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallServiceConstants.swift; sourceTree = ""; }; 40B21E611DADDEF00307E7AC /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; 4100DDE6BF3C566AB66B80CC /* MentionSuggestionItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionSuggestionItemView.swift; sourceTree = ""; }; + 4137900E28201C314C835C11 /* RoomScreenFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenFooterView.swift; sourceTree = ""; }; 4176C3E20C772DE8D182863C /* LegalInformationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreen.swift; sourceTree = ""; }; 419957D7B1C983D7B3B93678 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = ""; }; 41BB37D96C3EA18F3CE8675D /* RoomDirectorySearchScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreenModels.swift; sourceTree = ""; }; @@ -4018,6 +4020,7 @@ children = ( 422724361B6555364C43281E /* RoomHeaderView.swift */, 5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */, + 4137900E28201C314C835C11 /* RoomScreenFooterView.swift */, 4552D3466B1453F287223ADA /* SwipeRightAction.swift */, 464C6BFAA853DC755B9C1F60 /* PinnedItemsBanner */, ); @@ -6788,6 +6791,7 @@ F8F47CE757EE656905F01F2C /* RoomRolesAndPermissionsScreenViewModelProtocol.swift in Sources */, C55A44C99F64A479ABA85B46 /* RoomScreen.swift in Sources */, A851635B3255C6DC07034A12 /* RoomScreenCoordinator.swift in Sources */, + E8C65C19F7C40EE545172DD6 /* RoomScreenFooterView.swift in Sources */, 352C439BE0F75E101EF11FB1 /* RoomScreenModels.swift in Sources */, 7BB31E67648CF32D2AB5E502 /* RoomScreenViewModel.swift in Sources */, 617624A97BDBB75ED3DD8156 /* RoomScreenViewModelProtocol.swift in Sources */, @@ -7785,7 +7789,7 @@ repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.0.53; + version = 1.0.55; }; }; 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 77d6b4c460..864c38bde3 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/matrix-rust-components-swift", "state" : { - "revision" : "83abbdc8485340c20f27148153ff62f690ca210b", - "version" : "1.0.53" + "revision" : "8ee63edc76bccd12c17a22eaf4eddae69e5f1303", + "version" : "1.0.55" } }, { diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index 542d58057a..ec0dc26b69 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -124,6 +124,8 @@ final class AppSettings { let encryptionURL: URL = "https://element.io/help#encryption" /// A URL where users can go read more about the chat backup. let chatBackupDetailsURL: URL = "https://element.io/help#encryption5" + /// A URL where users can go read more about identity pinning violations + let identityPinningViolationDetailsURL: URL = "https://element.io/help#18" /// Any domains that Element web may be hosted on - used for handling links. let elementWebHosts = ["app.element.io", "staging.element.io", "develop.element.io"] diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index 5993152834..0df9e11bc5 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -590,7 +590,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { let composerDraftService = ComposerDraftService(roomProxy: roomProxy, timelineItemfactory: timelineItemFactory) - let parameters = RoomScreenCoordinatorParameters(roomProxy: roomProxy, + let parameters = RoomScreenCoordinatorParameters(clientProxy: userSession.clientProxy, + roomProxy: roomProxy, focussedEvent: focussedEvent, timelineController: timelineController, mediaProvider: userSession.mediaProvider, diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 356a7b892d..e4f2dadf38 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -4426,6 +4426,76 @@ class ClientProxyMock: ClientProxyProtocol { return curve25519Base64ReturnValue } } + //MARK: - pinUserIdentity + + var pinUserIdentityUnderlyingCallsCount = 0 + var pinUserIdentityCallsCount: Int { + get { + if Thread.isMainThread { + return pinUserIdentityUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = pinUserIdentityUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + pinUserIdentityUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + pinUserIdentityUnderlyingCallsCount = newValue + } + } + } + } + var pinUserIdentityCalled: Bool { + return pinUserIdentityCallsCount > 0 + } + var pinUserIdentityReceivedUserID: String? + var pinUserIdentityReceivedInvocations: [String] = [] + + var pinUserIdentityUnderlyingReturnValue: Result! + var pinUserIdentityReturnValue: Result! { + get { + if Thread.isMainThread { + return pinUserIdentityUnderlyingReturnValue + } else { + var returnValue: Result? = nil + DispatchQueue.main.sync { + returnValue = pinUserIdentityUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + pinUserIdentityUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + pinUserIdentityUnderlyingReturnValue = newValue + } + } + } + } + var pinUserIdentityClosure: ((String) async -> Result)? + + func pinUserIdentity(_ userID: String) async -> Result { + pinUserIdentityCallsCount += 1 + pinUserIdentityReceivedUserID = userID + DispatchQueue.main.async { + self.pinUserIdentityReceivedInvocations.append(userID) + } + if let pinUserIdentityClosure = pinUserIdentityClosure { + return await pinUserIdentityClosure(userID) + } else { + return pinUserIdentityReturnValue + } + } //MARK: - resetIdentity var resetIdentityUnderlyingCallsCount = 0 @@ -5791,6 +5861,11 @@ class JoinedRoomProxyMock: JoinedRoomProxyProtocol { set(value) { underlyingTypingMembersPublisher = value } } var underlyingTypingMembersPublisher: CurrentValuePublisher<[String], Never>! + var identityStatusChangesPublisher: CurrentValuePublisher<[IdentityStatusChange], Never> { + get { return underlyingIdentityStatusChangesPublisher } + set(value) { underlyingIdentityStatusChangesPublisher = value } + } + var underlyingIdentityStatusChangesPublisher: CurrentValuePublisher<[IdentityStatusChange], Never>! var actionsPublisher: AnyPublisher { get { return underlyingActionsPublisher } set(value) { underlyingActionsPublisher = value } @@ -12495,6 +12570,7 @@ class RoomMemberProxyMock: RoomMemberProxyProtocol { } var underlyingUserID: String! var displayName: String? + var disambiguatedDisplayName: String? var avatarURL: URL? var membership: MembershipState { get { return underlyingMembership } diff --git a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift index 4cd45da022..8469d7de3c 100644 --- a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift @@ -625,6 +625,52 @@ open class ClientSDKMock: MatrixRustSDK.Client { } } + //MARK: - customLoginWithJwt + + open var customLoginWithJwtJwtInitialDeviceNameDeviceIdThrowableError: Error? + var customLoginWithJwtJwtInitialDeviceNameDeviceIdUnderlyingCallsCount = 0 + open var customLoginWithJwtJwtInitialDeviceNameDeviceIdCallsCount: Int { + get { + if Thread.isMainThread { + return customLoginWithJwtJwtInitialDeviceNameDeviceIdUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = customLoginWithJwtJwtInitialDeviceNameDeviceIdUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + customLoginWithJwtJwtInitialDeviceNameDeviceIdUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + customLoginWithJwtJwtInitialDeviceNameDeviceIdUnderlyingCallsCount = newValue + } + } + } + } + open var customLoginWithJwtJwtInitialDeviceNameDeviceIdCalled: Bool { + return customLoginWithJwtJwtInitialDeviceNameDeviceIdCallsCount > 0 + } + open var customLoginWithJwtJwtInitialDeviceNameDeviceIdReceivedArguments: (jwt: String, initialDeviceName: String?, deviceId: String?)? + open var customLoginWithJwtJwtInitialDeviceNameDeviceIdReceivedInvocations: [(jwt: String, initialDeviceName: String?, deviceId: String?)] = [] + open var customLoginWithJwtJwtInitialDeviceNameDeviceIdClosure: ((String, String?, String?) async throws -> Void)? + + open override func customLoginWithJwt(jwt: String, initialDeviceName: String?, deviceId: String?) async throws { + if let error = customLoginWithJwtJwtInitialDeviceNameDeviceIdThrowableError { + throw error + } + customLoginWithJwtJwtInitialDeviceNameDeviceIdCallsCount += 1 + customLoginWithJwtJwtInitialDeviceNameDeviceIdReceivedArguments = (jwt: jwt, initialDeviceName: initialDeviceName, deviceId: deviceId) + DispatchQueue.main.async { + self.customLoginWithJwtJwtInitialDeviceNameDeviceIdReceivedInvocations.append((jwt: jwt, initialDeviceName: initialDeviceName, deviceId: deviceId)) + } + try await customLoginWithJwtJwtInitialDeviceNameDeviceIdClosure?(jwt, initialDeviceName, deviceId) + } + //MARK: - deactivateAccount open var deactivateAccountAuthDataEraseDataThrowableError: Error? @@ -4953,6 +4999,77 @@ open class ClientBuilderSDKMock: MatrixRustSDK.ClientBuilder { } } + //MARK: - roomDecryptionTrustRequirement + + var roomDecryptionTrustRequirementTrustRequirementUnderlyingCallsCount = 0 + open var roomDecryptionTrustRequirementTrustRequirementCallsCount: Int { + get { + if Thread.isMainThread { + return roomDecryptionTrustRequirementTrustRequirementUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = roomDecryptionTrustRequirementTrustRequirementUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + roomDecryptionTrustRequirementTrustRequirementUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + roomDecryptionTrustRequirementTrustRequirementUnderlyingCallsCount = newValue + } + } + } + } + open var roomDecryptionTrustRequirementTrustRequirementCalled: Bool { + return roomDecryptionTrustRequirementTrustRequirementCallsCount > 0 + } + open var roomDecryptionTrustRequirementTrustRequirementReceivedTrustRequirement: TrustRequirement? + open var roomDecryptionTrustRequirementTrustRequirementReceivedInvocations: [TrustRequirement] = [] + + var roomDecryptionTrustRequirementTrustRequirementUnderlyingReturnValue: ClientBuilder! + open var roomDecryptionTrustRequirementTrustRequirementReturnValue: ClientBuilder! { + get { + if Thread.isMainThread { + return roomDecryptionTrustRequirementTrustRequirementUnderlyingReturnValue + } else { + var returnValue: ClientBuilder? = nil + DispatchQueue.main.sync { + returnValue = roomDecryptionTrustRequirementTrustRequirementUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + roomDecryptionTrustRequirementTrustRequirementUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + roomDecryptionTrustRequirementTrustRequirementUnderlyingReturnValue = newValue + } + } + } + } + open var roomDecryptionTrustRequirementTrustRequirementClosure: ((TrustRequirement) -> ClientBuilder)? + + open override func roomDecryptionTrustRequirement(trustRequirement: TrustRequirement) -> ClientBuilder { + roomDecryptionTrustRequirementTrustRequirementCallsCount += 1 + roomDecryptionTrustRequirementTrustRequirementReceivedTrustRequirement = trustRequirement + DispatchQueue.main.async { + self.roomDecryptionTrustRequirementTrustRequirementReceivedInvocations.append(trustRequirement) + } + if let roomDecryptionTrustRequirementTrustRequirementClosure = roomDecryptionTrustRequirementTrustRequirementClosure { + return roomDecryptionTrustRequirementTrustRequirementClosure(trustRequirement) + } else { + return roomDecryptionTrustRequirementTrustRequirementReturnValue + } + } + //MARK: - roomKeyRecipientStrategy var roomKeyRecipientStrategyStrategyUnderlyingCallsCount = 0 @@ -12296,6 +12413,34 @@ open class RoomSDKMock: MatrixRustSDK.Room { } } + //MARK: - pinUserIdentity + + open var pinUserIdentityUserIdThrowableError: Error? + var pinUserIdentityUserIdUnderlyingCallsCount = 0 + open var pinUserIdentityUserIdCallsCount: Int { + get { + if Thread.isMainThread { + return pinUserIdentityUserIdUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = pinUserIdentityUserIdUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + pinUserIdentityUserIdUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + pinUserIdentityUserIdUnderlyingCallsCount = newValue + } + } + } + } + //MARK: - pinnedEventsTimeline open var pinnedEventsTimelineInternalIdPrefixMaxEventsToLoadMaxConcurrentRequestsThrowableError: Error? @@ -13068,6 +13213,77 @@ open class RoomSDKMock: MatrixRustSDK.Room { try await setUnreadFlagNewValueClosure?(newValue) } + //MARK: - subscribeToIdentityStatusChanges + + var subscribeToIdentityStatusChangesListenerUnderlyingCallsCount = 0 + open var subscribeToIdentityStatusChangesListenerCallsCount: Int { + get { + if Thread.isMainThread { + return subscribeToIdentityStatusChangesListenerUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = subscribeToIdentityStatusChangesListenerUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + subscribeToIdentityStatusChangesListenerUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + subscribeToIdentityStatusChangesListenerUnderlyingCallsCount = newValue + } + } + } + } + open var subscribeToIdentityStatusChangesListenerCalled: Bool { + return subscribeToIdentityStatusChangesListenerCallsCount > 0 + } + open var subscribeToIdentityStatusChangesListenerReceivedListener: IdentityStatusChangeListener? + open var subscribeToIdentityStatusChangesListenerReceivedInvocations: [IdentityStatusChangeListener] = [] + + var subscribeToIdentityStatusChangesListenerUnderlyingReturnValue: TaskHandle! + open var subscribeToIdentityStatusChangesListenerReturnValue: TaskHandle! { + get { + if Thread.isMainThread { + return subscribeToIdentityStatusChangesListenerUnderlyingReturnValue + } else { + var returnValue: TaskHandle? = nil + DispatchQueue.main.sync { + returnValue = subscribeToIdentityStatusChangesListenerUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + subscribeToIdentityStatusChangesListenerUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + subscribeToIdentityStatusChangesListenerUnderlyingReturnValue = newValue + } + } + } + } + open var subscribeToIdentityStatusChangesListenerClosure: ((IdentityStatusChangeListener) -> TaskHandle)? + + open override func subscribeToIdentityStatusChanges(listener: IdentityStatusChangeListener) -> TaskHandle { + subscribeToIdentityStatusChangesListenerCallsCount += 1 + subscribeToIdentityStatusChangesListenerReceivedListener = listener + DispatchQueue.main.async { + self.subscribeToIdentityStatusChangesListenerReceivedInvocations.append(listener) + } + if let subscribeToIdentityStatusChangesListenerClosure = subscribeToIdentityStatusChangesListenerClosure { + return subscribeToIdentityStatusChangesListenerClosure(listener) + } else { + return subscribeToIdentityStatusChangesListenerReturnValue + } + } + //MARK: - subscribeToRoomInfoUpdates var subscribeToRoomInfoUpdatesListenerUnderlyingCallsCount = 0 @@ -14087,77 +14303,6 @@ open class RoomListSDKMock: MatrixRustSDK.RoomList { fileprivate var pointer: UnsafeMutableRawPointer! - //MARK: - entries - - var entriesListenerUnderlyingCallsCount = 0 - open var entriesListenerCallsCount: Int { - get { - if Thread.isMainThread { - return entriesListenerUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = entriesListenerUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - entriesListenerUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - entriesListenerUnderlyingCallsCount = newValue - } - } - } - } - open var entriesListenerCalled: Bool { - return entriesListenerCallsCount > 0 - } - open var entriesListenerReceivedListener: RoomListEntriesListener? - open var entriesListenerReceivedInvocations: [RoomListEntriesListener] = [] - - var entriesListenerUnderlyingReturnValue: TaskHandle! - open var entriesListenerReturnValue: TaskHandle! { - get { - if Thread.isMainThread { - return entriesListenerUnderlyingReturnValue - } else { - var returnValue: TaskHandle? = nil - DispatchQueue.main.sync { - returnValue = entriesListenerUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - entriesListenerUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - entriesListenerUnderlyingReturnValue = newValue - } - } - } - } - open var entriesListenerClosure: ((RoomListEntriesListener) -> TaskHandle)? - - open override func entries(listener: RoomListEntriesListener) -> TaskHandle { - entriesListenerCallsCount += 1 - entriesListenerReceivedListener = listener - DispatchQueue.main.async { - self.entriesListenerReceivedInvocations.append(listener) - } - if let entriesListenerClosure = entriesListenerClosure { - return entriesListenerClosure(listener) - } else { - return entriesListenerReturnValue - } - } - //MARK: - entriesWithDynamicAdapters var entriesWithDynamicAdaptersPageSizeListenerUnderlyingCallsCount = 0 diff --git a/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift b/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift index 86e33a7a48..0855235a08 100644 --- a/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift +++ b/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift @@ -80,6 +80,7 @@ extension JoinedRoomProxyMock { membersPublisher = CurrentValueSubject(configuration.members).asCurrentValuePublisher() typingMembersPublisher = CurrentValueSubject([]).asCurrentValuePublisher() + identityStatusChangesPublisher = CurrentValueSubject([]).asCurrentValuePublisher() joinedMembersCount = configuration.members.filter { $0.membership == .join }.count activeMembersCount = configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count diff --git a/ElementX/Sources/Mocks/RoomMemberProxyMock.swift b/ElementX/Sources/Mocks/RoomMemberProxyMock.swift index 9825257d0a..2074c014b1 100644 --- a/ElementX/Sources/Mocks/RoomMemberProxyMock.swift +++ b/ElementX/Sources/Mocks/RoomMemberProxyMock.swift @@ -25,6 +25,11 @@ extension RoomMemberProxyMock { self.init() userID = configuration.userID displayName = configuration.displayName + + if let displayName = configuration.displayName { + disambiguatedDisplayName = "\(displayName) (\(userID))" + } + avatarURL = configuration.avatarURL membership = configuration.membership diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift index f1bbc1506d..16b4e2f495 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift @@ -12,6 +12,7 @@ import SwiftUI import WysiwygComposer struct RoomScreenCoordinatorParameters { + let clientProxy: ClientProxyProtocol let roomProxy: JoinedRoomProxyProtocol var focussedEvent: FocusEvent? let timelineController: RoomTimelineControllerProtocol @@ -61,13 +62,15 @@ final class RoomScreenCoordinator: CoordinatorProtocol { selectedPinnedEventID = focussedEvent.shouldSetPin ? focussedEvent.eventID : nil } - roomViewModel = RoomScreenViewModel(roomProxy: parameters.roomProxy, + roomViewModel = RoomScreenViewModel(clientProxy: parameters.clientProxy, + roomProxy: parameters.roomProxy, initialSelectedPinnedEventID: selectedPinnedEventID, mediaProvider: parameters.mediaProvider, ongoingCallRoomIDPublisher: parameters.ongoingCallRoomIDPublisher, appMediator: parameters.appMediator, appSettings: ServiceLocator.shared.settings, - analyticsService: ServiceLocator.shared.analytics) + analyticsService: ServiceLocator.shared.analytics, + userIndicatorController: ServiceLocator.shared.userIndicatorController) timelineViewModel = TimelineViewModel(roomProxy: parameters.roomProxy, focussedEventID: parameters.focussedEvent?.eventID, @@ -149,10 +152,10 @@ final class RoomScreenCoordinator: CoordinatorProtocol { .store(in: &cancellables) roomViewModel.actions - .sink { [weak self] actions in + .sink { [weak self] action in guard let self else { return } - switch actions { + switch action { case .focusEvent(eventID: let eventID): focusOnEvent(FocusEvent(eventID: eventID, shouldSetPin: false)) case .displayPinnedEventsTimeline: diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift index 9f4bb68a22..92c9bfdbe2 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift @@ -21,6 +21,7 @@ enum RoomScreenViewAction { case viewAllPins case displayRoomDetails case displayCall + case footerViewAction(RoomScreenFooterViewAction) } struct RoomScreenViewState: BindableState { @@ -38,11 +39,21 @@ struct RoomScreenViewState: BindableState { var hasOngoingCall: Bool var shouldShowCallButton = true + var footerDetails: RoomScreenFooterViewDetails? + var bindings: RoomScreenViewStateBindings } struct RoomScreenViewStateBindings { } +enum RoomScreenFooterViewAction { + case resolvePinViolation(userID: String) +} + +enum RoomScreenFooterViewDetails { + case pinViolation(member: RoomMemberProxyProtocol, learnMoreURL: URL) +} + enum PinnedEventsBannerState: Equatable { case loading(numbersOfEvents: Int) case loaded(state: PinnedEventsState) diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index 08beb380b2..36f3bb7239 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -7,18 +7,24 @@ import Combine import Foundation +import MatrixRustSDK import OrderedCollections import SwiftUI typealias RoomScreenViewModelType = StateStoreViewModel class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol { + private let clientProxy: ClientProxyProtocol private let roomProxy: JoinedRoomProxyProtocol private let appMediator: AppMediatorProtocol private let appSettings: AppSettings private let analyticsService: AnalyticsService - private let pinnedEventStringBuilder: RoomEventStringBuilder + private let userIndicatorController: UserIndicatorControllerProtocol + private var initialSelectedPinnedEventID: String? + private let pinnedEventStringBuilder: RoomEventStringBuilder + + private var identityPinningViolations = [String: RoomMemberProxyProtocol]() private let actionsSubject: PassthroughSubject = .init() var actions: AnyPublisher { @@ -43,17 +49,22 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } } - init(roomProxy: JoinedRoomProxyProtocol, + init(clientProxy: ClientProxyProtocol, + roomProxy: JoinedRoomProxyProtocol, initialSelectedPinnedEventID: String?, mediaProvider: MediaProviderProtocol, ongoingCallRoomIDPublisher: CurrentValuePublisher, appMediator: AppMediatorProtocol, appSettings: AppSettings, - analyticsService: AnalyticsService) { + analyticsService: AnalyticsService, + userIndicatorController: UserIndicatorControllerProtocol) { + self.clientProxy = clientProxy self.roomProxy = roomProxy self.appMediator = appMediator self.appSettings = appSettings self.analyticsService = analyticsService + self.userIndicatorController = userIndicatorController + self.initialSelectedPinnedEventID = initialSelectedPinnedEventID pinnedEventStringBuilder = .pinnedEventStringBuilder(userID: roomProxy.ownUserID) @@ -87,6 +98,11 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol actionsSubject.send(.displayCall) actionsSubject.send(.removeComposerFocus) analyticsService.trackInteraction(name: .MobileRoomCallButton) + case .footerViewAction(let action): + switch action { + case .resolvePinViolation(let userID): + Task { await resolveIdentityPinningViolation(userID) } + } } } @@ -98,6 +114,8 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol state.pinnedEventsBannerState.setSelectedPinnedEventID(eventID) } + // MARK: - Private + private func setupSubscriptions(ongoingCallRoomIDPublisher: CurrentValuePublisher) { let roomInfoSubscription = roomProxy .actionsPublisher @@ -124,6 +142,19 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } .store(in: &cancellables) + let identityStatusChangesPublisher = roomProxy.identityStatusChangesPublisher.receive(on: DispatchQueue.main) + + Task { [weak self] in + for await changes in identityStatusChangesPublisher.values { + guard !Task.isCancelled else { + return + } + + await self?.processIdentityStatusChanges(changes) + } + } + .store(in: &cancellables) + appMediator.networkMonitor.reachabilityPublisher .filter { $0 == .reachable } .receive(on: DispatchQueue.main) @@ -141,6 +172,43 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol .store(in: &cancellables) } + private func processIdentityStatusChanges(_ changes: [IdentityStatusChange]) async { + for change in changes { + switch change.changedTo { + case .pinned: + identityPinningViolations[change.userId] = nil + case .pinViolation: + guard case let .success(member) = await roomProxy.getMember(userID: change.userId) else { + MXLog.error("Failed retrieving room member for identity status change: \(change)") + continue + } + + identityPinningViolations[change.userId] = member + default: + break + } + } + + if let member = identityPinningViolations.values.first { + state.footerDetails = .pinViolation(member: member, + learnMoreURL: appSettings.identityPinningViolationDetailsURL) + } else { + state.footerDetails = nil + } + } + + private func resolveIdentityPinningViolation(_ userID: String) async { + defer { + hideLoadingIndicator() + } + + showLoadingIndicator() + + if case .failure = await clientProxy.pinUserIdentity(userID) { + userIndicatorController.alertInfo = .init(id: .init(), title: L10n.commonError) + } + } + private func buildPinnedEventContents(timelineItems: [TimelineItemProxy]) { var pinnedEventContents = OrderedDictionary() @@ -190,16 +258,30 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } } } + + // MARK: Loading indicators + + private static let loadingIndicatorIdentifier = "\(RoomScreenViewModel.self)-Loading" + + private func showLoadingIndicator() { + userIndicatorController.submitIndicator(.init(id: Self.loadingIndicatorIdentifier, type: .toast, title: L10n.commonLoading)) + } + + private func hideLoadingIndicator() { + userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier) + } } extension RoomScreenViewModel { static func mock(roomProxyMock: JoinedRoomProxyMock) -> RoomScreenViewModel { - RoomScreenViewModel(roomProxy: roomProxyMock, + RoomScreenViewModel(clientProxy: ClientProxyMock(), + roomProxy: roomProxyMock, initialSelectedPinnedEventID: nil, mediaProvider: MediaProviderMock(configuration: .init()), ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - analyticsService: ServiceLocator.shared.analytics) + analyticsService: ServiceLocator.shared.analytics, + userIndicatorController: ServiceLocator.shared.userIndicatorController) } } diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift index dee0516fb5..72a77e4480 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift @@ -29,21 +29,28 @@ struct RoomScreen: View { timeline .background(Color.compound.bgCanvasDefault.ignoresSafeArea()) .safeAreaInset(edge: .bottom, spacing: 0) { - composerToolbar - .padding(.bottom, composerToolbarContext.composerFormattingEnabled ? 8 : 12) - .background { - if composerToolbarContext.composerFormattingEnabled { - RoundedRectangle(cornerRadius: 20) - .stroke(Color.compound.borderInteractiveSecondary, lineWidth: 0.5) - .ignoresSafeArea() - } + VStack(spacing: 0) { + RoomScreenFooterView(details: roomContext.viewState.footerDetails, + mediaProvider: roomContext.mediaProvider) { action in + roomContext.send(viewAction: .footerViewAction(action)) } - .padding(.top, 8) - .background(Color.compound.bgCanvasDefault.ignoresSafeArea()) - .environmentObject(timelineContext) - .environment(\.timelineContext, timelineContext) - // Make sure the reply header honours the hideTimelineMedia setting too. - .environment(\.shouldAutomaticallyLoadImages, !timelineContext.viewState.hideTimelineMedia) + + composerToolbar + .padding(.bottom, composerToolbarContext.composerFormattingEnabled ? 8 : 12) + .background { + if composerToolbarContext.composerFormattingEnabled { + RoundedRectangle(cornerRadius: 20) + .stroke(Color.compound.borderInteractiveSecondary, lineWidth: 0.5) + .ignoresSafeArea() + } + } + .padding(.top, 8) + .background(Color.compound.bgCanvasDefault.ignoresSafeArea()) + .environmentObject(timelineContext) + .environment(\.timelineContext, timelineContext) + // Make sure the reply header honours the hideTimelineMedia setting too. + .environment(\.shouldAutomaticallyLoadImages, !timelineContext.viewState.hideTimelineMedia) + } } .overlay(alignment: .top) { Group { diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomScreenFooterView.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomScreenFooterView.swift new file mode 100644 index 0000000000..ec9e7df87f --- /dev/null +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomScreenFooterView.swift @@ -0,0 +1,78 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import SwiftUI + +struct RoomScreenFooterView: View { + let details: RoomScreenFooterViewDetails? + let mediaProvider: MediaProviderProtocol? + let callback: (RoomScreenFooterViewAction) -> Void + + var body: some View { + if let details { + ZStack(alignment: .top) { + VStack(spacing: 0) { + Color.compound.borderInfoSubtle + .frame(height: 1) + LinearGradient(colors: [.compound.bgInfoSubtle, .compound.bgCanvasDefault], + startPoint: .top, + endPoint: .bottom) + } + + switch details { + case .pinViolation(let member, let learnMoreURL): + pinViolation(member: member, learnMoreURL: learnMoreURL) + } + } + .padding(.top, 8) + .fixedSize(horizontal: false, vertical: true) + } + } + + private func pinViolation(member: RoomMemberProxyProtocol, + learnMoreURL: URL) -> some View { + VStack(spacing: 16) { + HStack(spacing: 16) { + LoadableAvatarImage(url: member.avatarURL, + name: member.disambiguatedDisplayName, + contentID: member.userID, + avatarSize: .user(on: .timeline), + mediaProvider: mediaProvider) + + Text(pinViolationDescriptionWithLearnMoreLink(displayName: member.disambiguatedDisplayName ?? member.userID, + url: learnMoreURL)) + .font(.compound.bodyMD) + .foregroundColor(.compound.textPrimary) + } + + Button(L10n.actionOk) { + callback(.resolvePinViolation(userID: member.userID)) + } + .buttonStyle(.compound(.primary, size: .medium)) + } + .padding(.top, 16) + .padding(.horizontal, 16) + .padding(.bottom, 8) + } + + private func pinViolationDescriptionWithLearnMoreLink(displayName: String, url: URL) -> AttributedString { + let linkPlaceholder = "{link}" + var description = AttributedString(L10n.cryptoIdentityChangePinViolation(displayName, linkPlaceholder)) + var linkString = AttributedString(L10n.actionLearnMore) + linkString.link = url + linkString.bold() + description.replace(linkPlaceholder, with: linkString) + return description + } +} + +struct RoomScreenFooterView_Previews: PreviewProvider, TestablePreview { + static var previews: some View { + RoomScreenFooterView(details: .pinViolation(member: RoomMemberProxyMock.mockBob, learnMoreURL: "https://element.io/"), + mediaProvider: MediaProviderMock(configuration: .init())) { _ in } + } +} diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 9a05854447..3ab6cfaac8 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -895,6 +895,22 @@ class ClientProxy: ClientProxyProtocol { await client.encryption().curve25519Key() } + func pinUserIdentity(_ userID: String) async -> Result { + MXLog.info("Pinning current identity for user: \(userID)") + + do { + guard let userIdentity = try await client.encryption().getUserIdentity(userId: userID) else { + MXLog.error("Failed retrieving identity for user: \(userID)") + return .failure(.failedRetrievingUserIdentity) + } + + return try await .success(userIdentity.pin()) + } catch { + MXLog.error("Failed pinning current identity for user: \(error)") + return .failure(.sdkError(error)) + } + } + func resetIdentity() async -> Result { do { return try await .success(client.encryption().resetIdentity()) diff --git a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift index 29a5a856cb..a942ec638e 100644 --- a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift +++ b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift @@ -35,6 +35,7 @@ enum ClientProxyError: Error { case invalidServerName case failedUploadingMedia(Error, MatrixErrorCode) case roomPreviewIsPrivate + case failedRetrievingUserIdentity } enum SlidingSyncConstants { @@ -196,5 +197,6 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol { func ed25519Base64() async -> String? func curve25519Base64() async -> String? + func pinUserIdentity(_ userID: String) async -> Result func resetIdentity() async -> Result } diff --git a/ElementX/Sources/Services/ElementCall/ElementCallService.swift b/ElementX/Sources/Services/ElementCall/ElementCallService.swift index 9555d62e8b..049f4c49bf 100644 --- a/ElementX/Sources/Services/ElementCall/ElementCallService.swift +++ b/ElementX/Sources/Services/ElementCall/ElementCallService.swift @@ -295,10 +295,12 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe roomProxy .actionsPublisher - .map { action -> (Bool, [String]) in + .compactMap { action -> (Bool, [String])? in switch action { case .roomInfoUpdate: return (roomProxy.hasOngoingCall, roomProxy.activeRoomCallParticipants) + default: + return nil } } .removeDuplicates { $0 == $1 } diff --git a/ElementX/Sources/Services/Room/JoinedRoomProxy.swift b/ElementX/Sources/Services/Room/JoinedRoomProxy.swift index b1fcd8bf7c..cdcd4fc19c 100644 --- a/ElementX/Sources/Services/Room/JoinedRoomProxy.swift +++ b/ElementX/Sources/Services/Room/JoinedRoomProxy.swift @@ -58,6 +58,8 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol { private var roomInfoObservationToken: TaskHandle? // periphery:ignore - required for instance retention in the rust codebase private var typingNotificationObservationToken: TaskHandle? + // periphery:ignore - required for instance retention in the rust codebase + private var identityStatusChangesObservationToken: TaskHandle? private var subscribedForUpdates = false @@ -70,6 +72,11 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol { var typingMembersPublisher: CurrentValuePublisher<[String], Never> { typingMembersSubject.asCurrentValuePublisher() } + + private let identityStatusChangesSubject = CurrentValueSubject<[IdentityStatusChange], Never>([]) + var identityStatusChangesPublisher: CurrentValuePublisher<[IdentityStatusChange], Never> { + identityStatusChangesSubject.asCurrentValuePublisher() + } private let actionsSubject = PassthroughSubject() var actionsPublisher: AnyPublisher { @@ -193,6 +200,8 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol { subscribeToRoomInfoUpdates() + subscribeToIdentityStatusChanges() + subscribeToTypingNotifications() } @@ -708,6 +717,16 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol { typingMembersSubject.send(typingMembers) }) } + + private func subscribeToIdentityStatusChanges() { + identityStatusChangesObservationToken = room.subscribeToIdentityStatusChanges(listener: RoomIdentityStatusChangeListener { [weak self] changes in + guard let self else { return } + + MXLog.info("Received identity status changes: \(changes)") + + identityStatusChangesSubject.send(changes) + }) + } } private final class RoomInfoUpdateListener: RoomInfoListener { @@ -733,3 +752,15 @@ private final class RoomTypingNotificationUpdateListener: TypingNotificationsLis onUpdateClosure(typingUserIds) } } + +private final class RoomIdentityStatusChangeListener: IdentityStatusChangeListener { + private let onUpdateClosure: ([IdentityStatusChange]) -> Void + + init(_ onUpdateClosure: @escaping ([IdentityStatusChange]) -> Void) { + self.onUpdateClosure = onUpdateClosure + } + + func call(identityStatusChange: [IdentityStatusChange]) { + onUpdateClosure(identityStatusChange) + } +} diff --git a/ElementX/Sources/Services/Room/RoomMember/RoomMemberProxy.swift b/ElementX/Sources/Services/Room/RoomMember/RoomMemberProxy.swift index 856ec67ae2..e583a83c14 100644 --- a/ElementX/Sources/Services/Room/RoomMember/RoomMemberProxy.swift +++ b/ElementX/Sources/Services/Room/RoomMember/RoomMemberProxy.swift @@ -16,12 +16,24 @@ final class RoomMemberProxy: RoomMemberProxyProtocol { } var userID: String { member.userId } + var displayName: String? { member.displayName } + + var disambiguatedDisplayName: String? { + guard let displayName else { + return nil + } + + return member.isNameAmbiguous ? "\(displayName) (\(userID))" : displayName + } + var avatarURL: URL? { member.avatarUrl.flatMap(URL.init(string:)) } var membership: MembershipState { member.membership } + var isIgnored: Bool { member.isIgnored } var powerLevel: Int { Int(member.powerLevel) } + var role: RoomMemberRole { member.suggestedRoleForPowerLevel } } diff --git a/ElementX/Sources/Services/Room/RoomMember/RoomMemberProxyProtocol.swift b/ElementX/Sources/Services/Room/RoomMember/RoomMemberProxyProtocol.swift index 6c1fe0afe2..817f6b57d4 100644 --- a/ElementX/Sources/Services/Room/RoomMember/RoomMemberProxyProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomMember/RoomMemberProxyProtocol.swift @@ -11,13 +11,18 @@ import MatrixRustSDK // sourcery: AutoMockable protocol RoomMemberProxyProtocol: AnyObject { var userID: String { get } + var displayName: String? { get } + var disambiguatedDisplayName: String? { get } + var avatarURL: URL? { get } var membership: MembershipState { get } + var isIgnored: Bool { get } var powerLevel: Int { get } + var role: RoomMemberRole { get } } diff --git a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift index d2d99406cb..552e0484d6 100644 --- a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift @@ -56,7 +56,7 @@ protocol InvitedRoomProxyProtocol: RoomProxyProtocol { func acceptInvitation() async -> Result } -enum JoinedRoomProxyAction { +enum JoinedRoomProxyAction: Equatable { case roomInfoUpdate } @@ -73,6 +73,8 @@ protocol JoinedRoomProxyProtocol: RoomProxyProtocol { var typingMembersPublisher: CurrentValuePublisher<[String], Never> { get } + var identityStatusChangesPublisher: CurrentValuePublisher<[IdentityStatusChange], Never> { get } + var actionsPublisher: AnyPublisher { get } var timeline: TimelineProxyProtocol { get } diff --git a/ElementX/Sources/Services/Timeline/TimelineItemContent/EncryptionAuthenticity.swift b/ElementX/Sources/Services/Timeline/TimelineItemContent/EncryptionAuthenticity.swift index 847e0ce9d5..67b50ea408 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItemContent/EncryptionAuthenticity.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItemContent/EncryptionAuthenticity.swift @@ -19,7 +19,7 @@ enum EncryptionAuthenticity: Hashable { case unknownDevice(color: Color) case unsignedDevice(color: Color) case unverifiedIdentity(color: Color) - case previouslyVerified(color: Color) + case verificationViolation(color: Color) case sentInClear(color: Color) var message: String { @@ -32,7 +32,7 @@ enum EncryptionAuthenticity: Hashable { L10n.eventShieldReasonUnsignedDevice case .unverifiedIdentity: L10n.eventShieldReasonUnverifiedIdentity - case .previouslyVerified: + case .verificationViolation: L10n.eventShieldReasonPreviouslyVerified case .sentInClear: L10n.eventShieldReasonSentInClear @@ -45,7 +45,7 @@ enum EncryptionAuthenticity: Hashable { .unknownDevice(let color), .unsignedDevice(let color), .unverifiedIdentity(let color), - .previouslyVerified(let color), + .verificationViolation(let color), .sentInClear(let color): color } @@ -54,7 +54,7 @@ enum EncryptionAuthenticity: Hashable { var icon: KeyPath { switch self { case .notGuaranteed: \.info - case .unknownDevice, .unsignedDevice, .unverifiedIdentity, .previouslyVerified: \.helpSolid + case .unknownDevice, .unsignedDevice, .unverifiedIdentity, .verificationViolation: \.helpSolid case .sentInClear: \.lockOff } } @@ -82,8 +82,8 @@ extension EncryptionAuthenticity { self = .unsignedDevice(color: color) case .unverifiedIdentity: self = .unverifiedIdentity(color: color) - case .previouslyVerified: - self = .previouslyVerified(color: color) + case .verificationViolation: + self = .verificationViolation(color: color) case .sentInClear: self = .sentInClear(color: color) } diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index 493e333020..1c125258bc 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -233,7 +233,8 @@ class MockScreen: Identifiable { return navigationStackCoordinator case .roomPlainNoAvatar: let navigationStackCoordinator = NavigationStackCoordinator() - let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Some room name", avatarURL: nil)), + let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), + roomProxy: JoinedRoomProxyMock(.init(name: "Some room name", avatarURL: nil)), timelineController: MockRoomTimelineController(), mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), @@ -251,7 +252,8 @@ class MockScreen: Identifiable { let navigationStackCoordinator = NavigationStackCoordinator() let timelineController = MockRoomTimelineController() timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk - let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: URL.picturesDirectory)), + let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), + roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: URL.picturesDirectory)), timelineController: timelineController, mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), @@ -269,7 +271,8 @@ class MockScreen: Identifiable { let navigationStackCoordinator = NavigationStackCoordinator() let timelineController = MockRoomTimelineController() timelineController.timelineItems = RoomTimelineItemFixtures.default - let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: URL.picturesDirectory)), + let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), + roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: URL.picturesDirectory)), timelineController: timelineController, mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), @@ -287,7 +290,8 @@ class MockScreen: Identifiable { let navigationStackCoordinator = NavigationStackCoordinator() let timelineController = MockRoomTimelineController() timelineController.timelineItems = RoomTimelineItemFixtures.smallChunkWithReadReceipts - let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: URL.picturesDirectory)), + let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), + roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: URL.picturesDirectory)), timelineController: timelineController, mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), @@ -308,7 +312,8 @@ class MockScreen: Identifiable { timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk timelineController.backPaginationResponses = [RoomTimelineItemFixtures.singleMessageChunk] timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage] - let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Small timeline", avatarURL: URL.picturesDirectory)), + let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), + roomProxy: JoinedRoomProxyMock(.init(name: "Small timeline", avatarURL: URL.picturesDirectory)), timelineController: timelineController, mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), @@ -329,7 +334,8 @@ class MockScreen: Identifiable { let timelineController = MockRoomTimelineController(listenForSignals: true) timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk] - let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Small timeline, paginating", avatarURL: URL.picturesDirectory)), + let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), + roomProxy: JoinedRoomProxyMock(.init(name: "Small timeline, paginating", avatarURL: URL.picturesDirectory)), timelineController: timelineController, mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), @@ -350,7 +356,8 @@ class MockScreen: Identifiable { let timelineController = MockRoomTimelineController(listenForSignals: true) timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk] - let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: URL.picturesDirectory)), + let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), + roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: URL.picturesDirectory)), timelineController: timelineController, mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), @@ -372,7 +379,8 @@ class MockScreen: Identifiable { timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk] timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage] - let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: URL.picturesDirectory)), + let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), + roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: URL.picturesDirectory)), timelineController: timelineController, mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), @@ -393,7 +401,8 @@ class MockScreen: Identifiable { let timelineController = MockRoomTimelineController(listenForSignals: true) timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage] - let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: URL.picturesDirectory)), + let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), + roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: URL.picturesDirectory)), timelineController: timelineController, mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), @@ -413,7 +422,8 @@ class MockScreen: Identifiable { let timelineController = MockRoomTimelineController() timelineController.timelineItems = RoomTimelineItemFixtures.permalinkChunk - let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Timeline highlight", avatarURL: URL.picturesDirectory)), + let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), + roomProxy: JoinedRoomProxyMock(.init(name: "Timeline highlight", avatarURL: URL.picturesDirectory)), timelineController: timelineController, mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), @@ -447,7 +457,8 @@ class MockScreen: Identifiable { let timelineController = MockRoomTimelineController() timelineController.timelineItems = RoomTimelineItemFixtures.disclosedPolls timelineController.incomingItems = [] - let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: URL.picturesDirectory)), + let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), + roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: URL.picturesDirectory)), timelineController: timelineController, mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), @@ -468,7 +479,8 @@ class MockScreen: Identifiable { let timelineController = MockRoomTimelineController() timelineController.timelineItems = RoomTimelineItemFixtures.undisclosedPolls timelineController.incomingItems = [] - let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: URL.picturesDirectory)), + let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), + roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: URL.picturesDirectory)), timelineController: timelineController, mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), @@ -489,7 +501,8 @@ class MockScreen: Identifiable { let timelineController = MockRoomTimelineController() timelineController.timelineItems = RoomTimelineItemFixtures.outgoingPolls timelineController.incomingItems = [] - let parameters = RoomScreenCoordinatorParameters(roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: URL.picturesDirectory)), + let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), + roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: URL.picturesDirectory)), timelineController: timelineController, mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), diff --git a/PreviewTests/Sources/GeneratedPreviewTests.swift b/PreviewTests/Sources/GeneratedPreviewTests.swift index 5819975f1c..615b072af3 100644 --- a/PreviewTests/Sources/GeneratedPreviewTests.swift +++ b/PreviewTests/Sources/GeneratedPreviewTests.swift @@ -689,6 +689,12 @@ extension PreviewTests { } } + func test_roomScreenFooterView() { + for preview in RoomScreenFooterView_Previews._allPreviews { + assertSnapshots(matching: preview) + } + } + func test_roomScreen() { for preview in RoomScreen_Previews._allPreviews { assertSnapshots(matching: preview) diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.1.png new file mode 100644 index 0000000000..43ba189302 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6ff5ac1cb8df1556b0d4ba1f119e65a182cb3ece6003f58db8a6129ef701dd94 +size 156835 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.1.png new file mode 100644 index 0000000000..8e484ff041 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b9693534082a1948f05d6f50c7e0cb8db768fdf6fe01189af4df64638edaae37 +size 166386 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.1.png new file mode 100644 index 0000000000..2019d816bd --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b02b43b978488a8e7b81d2767862a9a5289df638c502570557ef5f5e7a8dd4cc +size 77863 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.1.png new file mode 100644 index 0000000000..658879046e --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c7ab7912c6041b90042ed19c52ae21ee6ea4e88f5592b692607e7f8eb01adc40 +size 99897 diff --git a/UnitTests/Sources/LoggingTests.swift b/UnitTests/Sources/LoggingTests.swift index 99019ac587..66e069fab1 100644 --- a/UnitTests/Sources/LoggingTests.swift +++ b/UnitTests/Sources/LoggingTests.swift @@ -218,9 +218,32 @@ class LoggingTests: XCTestCase { let rustEmoteMessage = EmoteMessageContent(body: emoteString, formatted: FormattedBody(format: .html, body: "\(emoteString)")) - let rustImageMessage = ImageMessageContent(body: "ImageString", formatted: nil, filename: nil, source: MediaSource(noPointer: .init()), info: nil) - let rustVideoMessage = VideoMessageContent(body: "VideoString", formatted: nil, filename: nil, source: MediaSource(noPointer: .init()), info: nil) - let rustFileMessage = FileMessageContent(body: "FileString", formatted: nil, filename: "FileName", source: MediaSource(noPointer: .init()), info: nil) + let rustImageMessage = ImageMessageContent(body: "ImageString", + formatted: nil, + rawFilename: "ImageString", + filename: "ImageString", + caption: "ImageString", + formattedCaption: nil, + source: MediaSource(noPointer: .init()), + info: nil) + + let rustVideoMessage = VideoMessageContent(body: "VideoString", + formatted: nil, + rawFilename: "VideoString", + filename: "VideoString", + caption: "VideoString", + formattedCaption: nil, + source: MediaSource(noPointer: .init()), + info: nil) + + let rustFileMessage = FileMessageContent(body: "FileString", + formatted: nil, + rawFilename: "FileString", + filename: "FileString", + caption: "FileString", + formattedCaption: nil, + source: MediaSource(noPointer: .init()), + info: nil) // When logging that value MXLog.info(rustTextMessage) diff --git a/UnitTests/Sources/RoomScreenViewModelTests.swift b/UnitTests/Sources/RoomScreenViewModelTests.swift index da9196d2cb..43b8cf10c9 100644 --- a/UnitTests/Sources/RoomScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomScreenViewModelTests.swift @@ -33,13 +33,15 @@ class RoomScreenViewModelTests: XCTestCase { } // setup the room proxy actions publisher roomProxyMock.underlyingActionsPublisher = updateSubject.eraseToAnyPublisher() - let viewModel = RoomScreenViewModel(roomProxy: roomProxyMock, + let viewModel = RoomScreenViewModel(clientProxy: ClientProxyMock(), + roomProxy: roomProxyMock, initialSelectedPinnedEventID: nil, mediaProvider: MediaProviderMock(configuration: .init()), ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - analyticsService: ServiceLocator.shared.analytics) + analyticsService: ServiceLocator.shared.analytics, + userIndicatorController: ServiceLocator.shared.userIndicatorController) self.viewModel = viewModel // check if in the default state is not showing but is indeed loading @@ -111,13 +113,15 @@ class RoomScreenViewModelTests: XCTestCase { .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test2")), uniqueID: "2")), .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test3")), uniqueID: "3"))] roomProxyMock.underlyingPinnedEventsTimeline = pinnedTimelineMock - let viewModel = RoomScreenViewModel(roomProxy: roomProxyMock, + let viewModel = RoomScreenViewModel(clientProxy: ClientProxyMock(), + roomProxy: roomProxyMock, initialSelectedPinnedEventID: "test1", mediaProvider: MediaProviderMock(configuration: .init()), ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - analyticsService: ServiceLocator.shared.analytics) + analyticsService: ServiceLocator.shared.analytics, + userIndicatorController: ServiceLocator.shared.userIndicatorController) self.viewModel = viewModel // check if the banner is now in a loaded state and is showing the counter @@ -158,13 +162,15 @@ class RoomScreenViewModelTests: XCTestCase { // setup the room proxy actions publisher roomProxyMock.canUserJoinCallUserIDReturnValue = .success(false) roomProxyMock.underlyingActionsPublisher = updateSubject.eraseToAnyPublisher() - let viewModel = RoomScreenViewModel(roomProxy: roomProxyMock, + let viewModel = RoomScreenViewModel(clientProxy: ClientProxyMock(), + roomProxy: roomProxyMock, initialSelectedPinnedEventID: nil, mediaProvider: MediaProviderMock(configuration: .init()), ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - analyticsService: ServiceLocator.shared.analytics) + analyticsService: ServiceLocator.shared.analytics, + userIndicatorController: ServiceLocator.shared.userIndicatorController) self.viewModel = viewModel var deferred = deferFulfillment(viewModel.context.$viewState) { viewState in @@ -195,13 +201,15 @@ class RoomScreenViewModelTests: XCTestCase { // Given a room screen with no ongoing call. let ongoingCallRoomIDSubject = CurrentValueSubject(nil) let roomProxyMock = JoinedRoomProxyMock(.init(id: "MyRoomID")) - let viewModel = RoomScreenViewModel(roomProxy: roomProxyMock, + let viewModel = RoomScreenViewModel(clientProxy: ClientProxyMock(), + roomProxy: roomProxyMock, initialSelectedPinnedEventID: nil, mediaProvider: MediaProviderMock(configuration: .init()), ongoingCallRoomIDPublisher: ongoingCallRoomIDSubject.asCurrentValuePublisher(), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - analyticsService: ServiceLocator.shared.analytics) + analyticsService: ServiceLocator.shared.analytics, + userIndicatorController: ServiceLocator.shared.userIndicatorController) self.viewModel = viewModel XCTAssertTrue(viewModel.state.shouldShowCallButton) diff --git a/project.yml b/project.yml index 48265b0c99..d0c6ff5b5a 100644 --- a/project.yml +++ b/project.yml @@ -60,7 +60,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/element-hq/matrix-rust-components-swift - exactVersion: 1.0.53 + exactVersion: 1.0.55 # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios From 6e51969d27bf0c9240d389e32f517e48849ca23f Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Tue, 8 Oct 2024 11:00:58 +0100 Subject: [PATCH 035/114] Update the SDK and use media `filename` and `caption` internally. (#3375) Doesn't render captions (other than in fallback places). --- ElementX.xcodeproj/project.pbxproj | 5 + ElementX/Sources/Mocks/ClientProxyMock.swift | 2 +- .../Mocks/Generated/GeneratedMocks.swift | 152 ++++----- .../Mocks/Generated/SDKGeneratedMocks.swift | 191 +++++++++++ .../Sources/Mocks/MediaProviderMock.swift | 2 +- .../Other/SwiftUI/Views/LoadableImage.swift | 2 +- .../View/MessageComposer.swift | 19 +- .../Timeline/TimelineInteractionHandler.swift | 19 +- .../View/Replies/TimelineReplyView.swift | 31 +- .../Style/TimelineItemBubbledStylerView.swift | 321 +++++++----------- .../AudioRoomTimelineView.swift | 6 +- .../FileRoomTimelineView.swift | 15 +- .../ImageRoomTimelineView.swift | 10 +- .../VideoRoomTimelineView.swift | 17 +- .../Sources/Services/Client/ClientProxy.swift | 4 +- .../Services/Media/Provider/MediaLoader.swift | 4 +- .../Media/Provider/MediaLoaderProtocol.swift | 2 +- .../Media/Provider/MediaProvider.swift | 4 +- .../Provider/MediaProviderProtocol.swift | 4 +- .../Fixtures/RoomTimelineItemFixtures.swift | 4 +- .../Messages/AudioRoomTimelineItem.swift | 2 +- .../AudioRoomTimelineItemContent.swift | 6 +- .../Items/Messages/FileRoomTimelineItem.swift | 2 +- .../FileRoomTimelineItemContent.swift | 6 +- .../Messages/ImageRoomTimelineItem.swift | 2 +- .../ImageRoomTimelineItemContent.swift | 6 +- .../Messages/VideoRoomTimelineItem.swift | 2 +- .../VideoRoomTimelineItemContent.swift | 6 +- .../VoiceMessageRoomTimelineItem.swift | 2 +- .../VoiceMessageRoomTimelineView.swift | 2 +- .../RoomTimelineItemFactory.swift | 32 +- .../VoiceMessageMediaManager.swift | 2 +- ...Composer-iPad-en-GB.Replying-in-thread.png | 4 +- ...st_messageComposer-iPad-en-GB.Replying.png | 4 +- ...omposer-iPad-pseudo.Replying-in-thread.png | 4 +- ...t_messageComposer-iPad-pseudo.Replying.png | 4 +- ...ser-iPhone-16-en-GB.Replying-in-thread.png | 4 +- ...ssageComposer-iPhone-16-en-GB.Replying.png | 4 +- ...er-iPhone-16-pseudo.Replying-in-thread.png | 4 +- ...sageComposer-iPhone-16-pseudo.Replying.png | 4 +- UnitTests/Sources/LoggingTests.swift | 17 +- .../VoiceMessageMediaManagerTests.swift | 6 +- 42 files changed, 581 insertions(+), 358 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 38c2216c80..df1d323860 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -1921,6 +1921,7 @@ A84D413BF49F0E980F010A6B /* LogViewerScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogViewerScreenCoordinator.swift; sourceTree = ""; }; A861DA5932B128FE1DCB5CE2 /* InviteUsersScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersScreenCoordinator.swift; sourceTree = ""; }; A8DF55467ED4CE76B7AE9A33 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/InfoPlist.strings; sourceTree = ""; }; + A9873374E72AA53260AE90A2 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = fa.lproj/Localizable.strings; sourceTree = ""; }; A9B069D7772DDF6513E0F1B8 /* AuthenticationFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationFlowCoordinator.swift; sourceTree = ""; }; A9E6065FC6BC4A1B4C629E08 /* TimelineItemMenuActionProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemMenuActionProvider.swift; sourceTree = ""; }; A9FAFE1C2149E6AC8156ED2B /* Collection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Collection.swift; sourceTree = ""; }; @@ -2065,6 +2066,7 @@ C6A9F49B3EE59147AF2F70BB /* SeparatorRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorRoomTimelineItem.swift; sourceTree = ""; }; C6FEA87EA3752203065ECE27 /* BugReportUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportUITests.swift; sourceTree = ""; }; C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderAvatarImage.swift; sourceTree = ""; }; + C715CFE00686DACA59D836EA /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = fa.lproj/SAS.strings; sourceTree = ""; }; C729D95CB4588D4D9AAC3DFA /* RoomChangePermissionsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangePermissionsScreenModels.swift; sourceTree = ""; }; C733D11B421CFE3A657EF230 /* test_image.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = test_image.png; sourceTree = ""; }; C75EF87651B00A176AB08E97 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -5755,6 +5757,7 @@ en, es, et, + fa, fr, hu, id, @@ -7119,6 +7122,7 @@ 7447C0AD7EF302CD027D6230 /* en */, 6722709BD6178E10B70C9641 /* es */, F3C7252B3461D06175D975A4 /* et */, + C715CFE00686DACA59D836EA /* fa */, CEE20623EB4A9B88FB29F2BA /* fr */, D196116D2DD3F2757D45FCB7 /* hu */, 330AF4D121C3396F7A14B21D /* id */, @@ -7178,6 +7182,7 @@ CACA846B3E3E9A521D98B178 /* en */, CBBCC6E74774E79B599625D0 /* es */, A443FAE2EE820A5790C35C8D /* et */, + A9873374E72AA53260AE90A2 /* fa */, CC680E0E79D818706CB28CF8 /* fr */, 624244C398804ADC885239AA /* hu */, EF98A02DED04075F7CF0C721 /* id */, diff --git a/ElementX/Sources/Mocks/ClientProxyMock.swift b/ElementX/Sources/Mocks/ClientProxyMock.swift index e727ba6af3..825588e479 100644 --- a/ElementX/Sources/Mocks/ClientProxyMock.swift +++ b/ElementX/Sources/Mocks/ClientProxyMock.swift @@ -77,7 +77,7 @@ extension ClientProxyMock { loadMediaContentForSourceThrowableError = ClientProxyError.sdkError(ClientProxyMockError.generic) loadMediaThumbnailForSourceWidthHeightThrowableError = ClientProxyError.sdkError(ClientProxyMockError.generic) - loadMediaFileForSourceBodyThrowableError = ClientProxyError.sdkError(ClientProxyMockError.generic) + loadMediaFileForSourceFilenameThrowableError = ClientProxyError.sdkError(ClientProxyMockError.generic) secureBackupController = { let secureBackupController = SecureBackupControllerMock() diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index e4f2dadf38..c9f2da6433 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -4710,16 +4710,16 @@ class ClientProxyMock: ClientProxyProtocol { } //MARK: - loadMediaFileForSource - var loadMediaFileForSourceBodyThrowableError: Error? - var loadMediaFileForSourceBodyUnderlyingCallsCount = 0 - var loadMediaFileForSourceBodyCallsCount: Int { + var loadMediaFileForSourceFilenameThrowableError: Error? + var loadMediaFileForSourceFilenameUnderlyingCallsCount = 0 + var loadMediaFileForSourceFilenameCallsCount: Int { get { if Thread.isMainThread { - return loadMediaFileForSourceBodyUnderlyingCallsCount + return loadMediaFileForSourceFilenameUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = loadMediaFileForSourceBodyUnderlyingCallsCount + returnValue = loadMediaFileForSourceFilenameUnderlyingCallsCount } return returnValue! @@ -4727,29 +4727,29 @@ class ClientProxyMock: ClientProxyProtocol { } set { if Thread.isMainThread { - loadMediaFileForSourceBodyUnderlyingCallsCount = newValue + loadMediaFileForSourceFilenameUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - loadMediaFileForSourceBodyUnderlyingCallsCount = newValue + loadMediaFileForSourceFilenameUnderlyingCallsCount = newValue } } } } - var loadMediaFileForSourceBodyCalled: Bool { - return loadMediaFileForSourceBodyCallsCount > 0 + var loadMediaFileForSourceFilenameCalled: Bool { + return loadMediaFileForSourceFilenameCallsCount > 0 } - var loadMediaFileForSourceBodyReceivedArguments: (source: MediaSourceProxy, body: String?)? - var loadMediaFileForSourceBodyReceivedInvocations: [(source: MediaSourceProxy, body: String?)] = [] + var loadMediaFileForSourceFilenameReceivedArguments: (source: MediaSourceProxy, filename: String?)? + var loadMediaFileForSourceFilenameReceivedInvocations: [(source: MediaSourceProxy, filename: String?)] = [] - var loadMediaFileForSourceBodyUnderlyingReturnValue: MediaFileHandleProxy! - var loadMediaFileForSourceBodyReturnValue: MediaFileHandleProxy! { + var loadMediaFileForSourceFilenameUnderlyingReturnValue: MediaFileHandleProxy! + var loadMediaFileForSourceFilenameReturnValue: MediaFileHandleProxy! { get { if Thread.isMainThread { - return loadMediaFileForSourceBodyUnderlyingReturnValue + return loadMediaFileForSourceFilenameUnderlyingReturnValue } else { var returnValue: MediaFileHandleProxy? = nil DispatchQueue.main.sync { - returnValue = loadMediaFileForSourceBodyUnderlyingReturnValue + returnValue = loadMediaFileForSourceFilenameUnderlyingReturnValue } return returnValue! @@ -4757,29 +4757,29 @@ class ClientProxyMock: ClientProxyProtocol { } set { if Thread.isMainThread { - loadMediaFileForSourceBodyUnderlyingReturnValue = newValue + loadMediaFileForSourceFilenameUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - loadMediaFileForSourceBodyUnderlyingReturnValue = newValue + loadMediaFileForSourceFilenameUnderlyingReturnValue = newValue } } } } - var loadMediaFileForSourceBodyClosure: ((MediaSourceProxy, String?) async throws -> MediaFileHandleProxy)? + var loadMediaFileForSourceFilenameClosure: ((MediaSourceProxy, String?) async throws -> MediaFileHandleProxy)? - func loadMediaFileForSource(_ source: MediaSourceProxy, body: String?) async throws -> MediaFileHandleProxy { - if let error = loadMediaFileForSourceBodyThrowableError { + func loadMediaFileForSource(_ source: MediaSourceProxy, filename: String?) async throws -> MediaFileHandleProxy { + if let error = loadMediaFileForSourceFilenameThrowableError { throw error } - loadMediaFileForSourceBodyCallsCount += 1 - loadMediaFileForSourceBodyReceivedArguments = (source: source, body: body) + loadMediaFileForSourceFilenameCallsCount += 1 + loadMediaFileForSourceFilenameReceivedArguments = (source: source, filename: filename) DispatchQueue.main.async { - self.loadMediaFileForSourceBodyReceivedInvocations.append((source: source, body: body)) + self.loadMediaFileForSourceFilenameReceivedInvocations.append((source: source, filename: filename)) } - if let loadMediaFileForSourceBodyClosure = loadMediaFileForSourceBodyClosure { - return try await loadMediaFileForSourceBodyClosure(source, body) + if let loadMediaFileForSourceFilenameClosure = loadMediaFileForSourceFilenameClosure { + return try await loadMediaFileForSourceFilenameClosure(source, filename) } else { - return loadMediaFileForSourceBodyReturnValue + return loadMediaFileForSourceFilenameReturnValue } } } @@ -9693,16 +9693,16 @@ class MediaLoaderMock: MediaLoaderProtocol { } //MARK: - loadMediaFileForSource - var loadMediaFileForSourceBodyThrowableError: Error? - var loadMediaFileForSourceBodyUnderlyingCallsCount = 0 - var loadMediaFileForSourceBodyCallsCount: Int { + var loadMediaFileForSourceFilenameThrowableError: Error? + var loadMediaFileForSourceFilenameUnderlyingCallsCount = 0 + var loadMediaFileForSourceFilenameCallsCount: Int { get { if Thread.isMainThread { - return loadMediaFileForSourceBodyUnderlyingCallsCount + return loadMediaFileForSourceFilenameUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = loadMediaFileForSourceBodyUnderlyingCallsCount + returnValue = loadMediaFileForSourceFilenameUnderlyingCallsCount } return returnValue! @@ -9710,29 +9710,29 @@ class MediaLoaderMock: MediaLoaderProtocol { } set { if Thread.isMainThread { - loadMediaFileForSourceBodyUnderlyingCallsCount = newValue + loadMediaFileForSourceFilenameUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - loadMediaFileForSourceBodyUnderlyingCallsCount = newValue + loadMediaFileForSourceFilenameUnderlyingCallsCount = newValue } } } } - var loadMediaFileForSourceBodyCalled: Bool { - return loadMediaFileForSourceBodyCallsCount > 0 + var loadMediaFileForSourceFilenameCalled: Bool { + return loadMediaFileForSourceFilenameCallsCount > 0 } - var loadMediaFileForSourceBodyReceivedArguments: (source: MediaSourceProxy, body: String?)? - var loadMediaFileForSourceBodyReceivedInvocations: [(source: MediaSourceProxy, body: String?)] = [] + var loadMediaFileForSourceFilenameReceivedArguments: (source: MediaSourceProxy, filename: String?)? + var loadMediaFileForSourceFilenameReceivedInvocations: [(source: MediaSourceProxy, filename: String?)] = [] - var loadMediaFileForSourceBodyUnderlyingReturnValue: MediaFileHandleProxy! - var loadMediaFileForSourceBodyReturnValue: MediaFileHandleProxy! { + var loadMediaFileForSourceFilenameUnderlyingReturnValue: MediaFileHandleProxy! + var loadMediaFileForSourceFilenameReturnValue: MediaFileHandleProxy! { get { if Thread.isMainThread { - return loadMediaFileForSourceBodyUnderlyingReturnValue + return loadMediaFileForSourceFilenameUnderlyingReturnValue } else { var returnValue: MediaFileHandleProxy? = nil DispatchQueue.main.sync { - returnValue = loadMediaFileForSourceBodyUnderlyingReturnValue + returnValue = loadMediaFileForSourceFilenameUnderlyingReturnValue } return returnValue! @@ -9740,29 +9740,29 @@ class MediaLoaderMock: MediaLoaderProtocol { } set { if Thread.isMainThread { - loadMediaFileForSourceBodyUnderlyingReturnValue = newValue + loadMediaFileForSourceFilenameUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - loadMediaFileForSourceBodyUnderlyingReturnValue = newValue + loadMediaFileForSourceFilenameUnderlyingReturnValue = newValue } } } } - var loadMediaFileForSourceBodyClosure: ((MediaSourceProxy, String?) async throws -> MediaFileHandleProxy)? + var loadMediaFileForSourceFilenameClosure: ((MediaSourceProxy, String?) async throws -> MediaFileHandleProxy)? - func loadMediaFileForSource(_ source: MediaSourceProxy, body: String?) async throws -> MediaFileHandleProxy { - if let error = loadMediaFileForSourceBodyThrowableError { + func loadMediaFileForSource(_ source: MediaSourceProxy, filename: String?) async throws -> MediaFileHandleProxy { + if let error = loadMediaFileForSourceFilenameThrowableError { throw error } - loadMediaFileForSourceBodyCallsCount += 1 - loadMediaFileForSourceBodyReceivedArguments = (source: source, body: body) + loadMediaFileForSourceFilenameCallsCount += 1 + loadMediaFileForSourceFilenameReceivedArguments = (source: source, filename: filename) DispatchQueue.main.async { - self.loadMediaFileForSourceBodyReceivedInvocations.append((source: source, body: body)) + self.loadMediaFileForSourceFilenameReceivedInvocations.append((source: source, filename: filename)) } - if let loadMediaFileForSourceBodyClosure = loadMediaFileForSourceBodyClosure { - return try await loadMediaFileForSourceBodyClosure(source, body) + if let loadMediaFileForSourceFilenameClosure = loadMediaFileForSourceFilenameClosure { + return try await loadMediaFileForSourceFilenameClosure(source, filename) } else { - return loadMediaFileForSourceBodyReturnValue + return loadMediaFileForSourceFilenameReturnValue } } } @@ -10628,15 +10628,15 @@ class MediaProviderMock: MediaProviderProtocol { } //MARK: - loadFileFromSource - var loadFileFromSourceBodyUnderlyingCallsCount = 0 - var loadFileFromSourceBodyCallsCount: Int { + var loadFileFromSourceFilenameUnderlyingCallsCount = 0 + var loadFileFromSourceFilenameCallsCount: Int { get { if Thread.isMainThread { - return loadFileFromSourceBodyUnderlyingCallsCount + return loadFileFromSourceFilenameUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = loadFileFromSourceBodyUnderlyingCallsCount + returnValue = loadFileFromSourceFilenameUnderlyingCallsCount } return returnValue! @@ -10644,29 +10644,29 @@ class MediaProviderMock: MediaProviderProtocol { } set { if Thread.isMainThread { - loadFileFromSourceBodyUnderlyingCallsCount = newValue + loadFileFromSourceFilenameUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - loadFileFromSourceBodyUnderlyingCallsCount = newValue + loadFileFromSourceFilenameUnderlyingCallsCount = newValue } } } } - var loadFileFromSourceBodyCalled: Bool { - return loadFileFromSourceBodyCallsCount > 0 + var loadFileFromSourceFilenameCalled: Bool { + return loadFileFromSourceFilenameCallsCount > 0 } - var loadFileFromSourceBodyReceivedArguments: (source: MediaSourceProxy, body: String?)? - var loadFileFromSourceBodyReceivedInvocations: [(source: MediaSourceProxy, body: String?)] = [] + var loadFileFromSourceFilenameReceivedArguments: (source: MediaSourceProxy, filename: String?)? + var loadFileFromSourceFilenameReceivedInvocations: [(source: MediaSourceProxy, filename: String?)] = [] - var loadFileFromSourceBodyUnderlyingReturnValue: Result! - var loadFileFromSourceBodyReturnValue: Result! { + var loadFileFromSourceFilenameUnderlyingReturnValue: Result! + var loadFileFromSourceFilenameReturnValue: Result! { get { if Thread.isMainThread { - return loadFileFromSourceBodyUnderlyingReturnValue + return loadFileFromSourceFilenameUnderlyingReturnValue } else { var returnValue: Result? = nil DispatchQueue.main.sync { - returnValue = loadFileFromSourceBodyUnderlyingReturnValue + returnValue = loadFileFromSourceFilenameUnderlyingReturnValue } return returnValue! @@ -10674,26 +10674,26 @@ class MediaProviderMock: MediaProviderProtocol { } set { if Thread.isMainThread { - loadFileFromSourceBodyUnderlyingReturnValue = newValue + loadFileFromSourceFilenameUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - loadFileFromSourceBodyUnderlyingReturnValue = newValue + loadFileFromSourceFilenameUnderlyingReturnValue = newValue } } } } - var loadFileFromSourceBodyClosure: ((MediaSourceProxy, String?) async -> Result)? + var loadFileFromSourceFilenameClosure: ((MediaSourceProxy, String?) async -> Result)? - func loadFileFromSource(_ source: MediaSourceProxy, body: String?) async -> Result { - loadFileFromSourceBodyCallsCount += 1 - loadFileFromSourceBodyReceivedArguments = (source: source, body: body) + func loadFileFromSource(_ source: MediaSourceProxy, filename: String?) async -> Result { + loadFileFromSourceFilenameCallsCount += 1 + loadFileFromSourceFilenameReceivedArguments = (source: source, filename: filename) DispatchQueue.main.async { - self.loadFileFromSourceBodyReceivedInvocations.append((source: source, body: body)) + self.loadFileFromSourceFilenameReceivedInvocations.append((source: source, filename: filename)) } - if let loadFileFromSourceBodyClosure = loadFileFromSourceBodyClosure { - return await loadFileFromSourceBodyClosure(source, body) + if let loadFileFromSourceFilenameClosure = loadFileFromSourceFilenameClosure { + return await loadFileFromSourceFilenameClosure(source, filename) } else { - return loadFileFromSourceBodyReturnValue + return loadFileFromSourceFilenameReturnValue } } } diff --git a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift index 8469d7de3c..da47bc6dc2 100644 --- a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift @@ -6139,6 +6139,81 @@ open class EncryptionSDKMock: MatrixRustSDK.Encryption { } } + //MARK: - getUserIdentity + + open var getUserIdentityUserIdThrowableError: Error? + var getUserIdentityUserIdUnderlyingCallsCount = 0 + open var getUserIdentityUserIdCallsCount: Int { + get { + if Thread.isMainThread { + return getUserIdentityUserIdUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = getUserIdentityUserIdUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + getUserIdentityUserIdUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + getUserIdentityUserIdUnderlyingCallsCount = newValue + } + } + } + } + open var getUserIdentityUserIdCalled: Bool { + return getUserIdentityUserIdCallsCount > 0 + } + open var getUserIdentityUserIdReceivedUserId: String? + open var getUserIdentityUserIdReceivedInvocations: [String] = [] + + var getUserIdentityUserIdUnderlyingReturnValue: UserIdentity? + open var getUserIdentityUserIdReturnValue: UserIdentity? { + get { + if Thread.isMainThread { + return getUserIdentityUserIdUnderlyingReturnValue + } else { + var returnValue: UserIdentity?? = nil + DispatchQueue.main.sync { + returnValue = getUserIdentityUserIdUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + getUserIdentityUserIdUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + getUserIdentityUserIdUnderlyingReturnValue = newValue + } + } + } + } + open var getUserIdentityUserIdClosure: ((String) async throws -> UserIdentity?)? + + open override func getUserIdentity(userId: String) async throws -> UserIdentity? { + if let error = getUserIdentityUserIdThrowableError { + throw error + } + getUserIdentityUserIdCallsCount += 1 + getUserIdentityUserIdReceivedUserId = userId + DispatchQueue.main.async { + self.getUserIdentityUserIdReceivedInvocations.append(userId) + } + if let getUserIdentityUserIdClosure = getUserIdentityUserIdClosure { + return try await getUserIdentityUserIdClosure(userId) + } else { + return getUserIdentityUserIdReturnValue + } + } + //MARK: - isLastDevice open var isLastDeviceThrowableError: Error? @@ -20854,6 +20929,122 @@ open class UnreadNotificationsCountSDKMock: MatrixRustSDK.UnreadNotificationsCou } } } +open class UserIdentitySDKMock: MatrixRustSDK.UserIdentity { + init() { + super.init(noPointer: .init()) + } + + public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + fatalError("init(unsafeFromRawPointer:) has not been implemented") + } + + fileprivate var pointer: UnsafeMutableRawPointer! + + //MARK: - masterKey + + var masterKeyUnderlyingCallsCount = 0 + open var masterKeyCallsCount: Int { + get { + if Thread.isMainThread { + return masterKeyUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = masterKeyUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + masterKeyUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + masterKeyUnderlyingCallsCount = newValue + } + } + } + } + open var masterKeyCalled: Bool { + return masterKeyCallsCount > 0 + } + + var masterKeyUnderlyingReturnValue: String? + open var masterKeyReturnValue: String? { + get { + if Thread.isMainThread { + return masterKeyUnderlyingReturnValue + } else { + var returnValue: String?? = nil + DispatchQueue.main.sync { + returnValue = masterKeyUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + masterKeyUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + masterKeyUnderlyingReturnValue = newValue + } + } + } + } + open var masterKeyClosure: (() -> String?)? + + open override func masterKey() -> String? { + masterKeyCallsCount += 1 + if let masterKeyClosure = masterKeyClosure { + return masterKeyClosure() + } else { + return masterKeyReturnValue + } + } + + //MARK: - pin + + open var pinThrowableError: Error? + var pinUnderlyingCallsCount = 0 + open var pinCallsCount: Int { + get { + if Thread.isMainThread { + return pinUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = pinUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + pinUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + pinUnderlyingCallsCount = newValue + } + } + } + } + open var pinCalled: Bool { + return pinCallsCount > 0 + } + open var pinClosure: (() async throws -> Void)? + + open override func pin() async throws { + if let error = pinThrowableError { + throw error + } + pinCallsCount += 1 + try await pinClosure?() + } +} open class WidgetDriverSDKMock: MatrixRustSDK.WidgetDriver { init() { super.init(noPointer: .init()) diff --git a/ElementX/Sources/Mocks/MediaProviderMock.swift b/ElementX/Sources/Mocks/MediaProviderMock.swift index da7a26909f..eb1929eff4 100644 --- a/ElementX/Sources/Mocks/MediaProviderMock.swift +++ b/ElementX/Sources/Mocks/MediaProviderMock.swift @@ -42,7 +42,7 @@ extension MediaProviderMock { return .success(data) } - loadFileFromSourceBodyReturnValue = .failure(.failedRetrievingFile) + loadFileFromSourceFilenameReturnValue = .failure(.failedRetrievingFile) loadImageRetryingOnReconnectionSizeClosure = { _, _ in Task { diff --git a/ElementX/Sources/Other/SwiftUI/Views/LoadableImage.swift b/ElementX/Sources/Other/SwiftUI/Views/LoadableImage.swift index 6bf098b997..1f909688bb 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/LoadableImage.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/LoadableImage.swift @@ -386,7 +386,7 @@ struct LoadableImage_Previews: PreviewProvider, TestablePreview { if isLoading { mediaProvider.imageFromSourceSizeClosure = { _, _ in nil } - mediaProvider.loadFileFromSourceBodyClosure = { _, _ in .failure(.failedRetrievingFile) } + mediaProvider.loadFileFromSourceFilenameClosure = { _, _ in .failure(.failedRetrievingFile) } mediaProvider.loadImageDataFromSourceClosure = { _ in .failure(.failedRetrievingImage) } mediaProvider.loadImageFromSourceSizeClosure = { _, _ in .failure(.failedRetrievingImage) } mediaProvider.loadThumbnailForSourceSourceSizeClosure = { _, _ in .failure(.failedRetrievingThumbnail) } diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift index 85e0fd018d..61a3a6911c 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift @@ -206,16 +206,26 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview { static let replyTypes: [TimelineItemReplyDetails] = [ .loaded(sender: .init(id: "Dave"), eventID: "123", - eventContent: .message(.audio(.init(body: "Audio: Ride the lightning", duration: 100, waveform: nil, source: nil, contentType: nil)))), + eventContent: .message(.audio(.init(filename: "lightning.mp3", + caption: "Audio: Ride the lightning", + duration: 100, + waveform: nil, + source: nil, + contentType: nil)))), .loaded(sender: .init(id: "James"), eventID: "123", eventContent: .message(.emote(.init(body: "Emote: James thinks he's the phantom lord")))), .loaded(sender: .init(id: "Robert"), eventID: "123", - eventContent: .message(.file(.init(body: "File: Crash course in brain surgery.pdf", source: nil, thumbnailSource: nil, contentType: nil)))), + eventContent: .message(.file(.init(filename: "brain-surgery.pdf", + caption: "File: Crash course in brain surgery", + source: nil, + thumbnailSource: nil, + contentType: nil)))), .loaded(sender: .init(id: "Cliff"), eventID: "123", - eventContent: .message(.image(.init(body: "Image: Pushead", + eventContent: .message(.image(.init(filename: "head.png", + caption: "Image: Pushead", source: .init(url: .picturesDirectory, mimeType: nil), thumbnailSource: .init(url: .picturesDirectory, mimeType: nil))))), .loaded(sender: .init(id: "Jason"), @@ -226,7 +236,8 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview { eventContent: .message(.text(.init(body: "Text: Where the wild things are")))), .loaded(sender: .init(id: "Lars"), eventID: "123", - eventContent: .message(.video(.init(body: "Video: Through the never", + eventContent: .message(.video(.init(filename: "never.mov", + caption: "Video: Through the never", duration: 100, source: nil, thumbnailSource: .init(url: .picturesDirectory, mimeType: nil))))), diff --git a/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift b/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift index b4ad60281e..7696f2501a 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift @@ -532,30 +532,35 @@ class TimelineInteractionHandler { private func displayMediaActionIfPossible(timelineItem: RoomTimelineItemProtocol) async -> RoomTimelineControllerAction { var source: MediaSourceProxy? - var body: String + var filename: String + var caption: String? switch timelineItem { case let item as ImageRoomTimelineItem: source = item.content.source - body = item.content.body + filename = item.content.filename + caption = item.content.caption case let item as VideoRoomTimelineItem: source = item.content.source - body = item.content.body + filename = item.content.filename + caption = item.content.caption case let item as FileRoomTimelineItem: source = item.content.source - body = item.content.body + filename = item.content.filename + caption = item.content.caption case let item as AudioRoomTimelineItem: // For now we are just displaying audio messages with the File preview until we create a timeline player for them. source = item.content.source - body = item.content.body + filename = item.content.filename + caption = item.content.caption default: return .none } guard let source else { return .none } - switch await mediaProvider.loadFileFromSource(source, body: body) { + switch await mediaProvider.loadFileFromSource(source, filename: filename) { case .success(let file): - return .displayMediaFile(file: file, title: body) + return .displayMediaFile(file: file, title: caption ?? filename) case .failure: return .none } diff --git a/ElementX/Sources/Screens/Timeline/View/Replies/TimelineReplyView.swift b/ElementX/Sources/Screens/Timeline/View/Replies/TimelineReplyView.swift index 0a1b88bd8c..6326e84a32 100644 --- a/ElementX/Sources/Screens/Timeline/View/Replies/TimelineReplyView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Replies/TimelineReplyView.swift @@ -26,8 +26,8 @@ struct TimelineReplyView: View { switch content { case .audio(let content): ReplyView(sender: sender, - plainBody: content.body, - formattedBody: nil, + plainBody: content.caption ?? content.filename, + formattedBody: content.formattedCaption, icon: .init(kind: .systemIcon("waveform"), cornerRadii: iconCornerRadii)) case .emote(let content): ReplyView(sender: sender, @@ -35,13 +35,13 @@ struct TimelineReplyView: View { formattedBody: content.formattedBody) case .file(let content): ReplyView(sender: sender, - plainBody: content.body, - formattedBody: nil, + plainBody: content.caption ?? content.filename, + formattedBody: content.formattedCaption, icon: .init(kind: .icon(\.document), cornerRadii: iconCornerRadii)) case .image(let content): ReplyView(sender: sender, - plainBody: content.body, - formattedBody: nil, + plainBody: content.caption ?? content.filename, + formattedBody: content.formattedCaption, icon: .init(kind: .mediaSource(content.thumbnailSource ?? content.source), cornerRadii: iconCornerRadii)) case .notice(let content): ReplyView(sender: sender, @@ -53,8 +53,8 @@ struct TimelineReplyView: View { formattedBody: content.formattedBody) case .video(let content): ReplyView(sender: sender, - plainBody: content.body, - formattedBody: nil, + plainBody: content.caption ?? content.filename, + formattedBody: content.formattedCaption, icon: content.thumbnailSource.map { .init(kind: .mediaSource($0), cornerRadii: iconCornerRadii) }) case .voice: ReplyView(sender: sender, @@ -247,7 +247,8 @@ struct TimelineReplyView_Previews: PreviewProvider, TestablePreview { TimelineReplyView(placement: .timeline, timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"), eventID: "123", - eventContent: .message(.audio(.init(body: "Some audio", + eventContent: .message(.audio(.init(filename: "audio.m4a", + caption: "Some audio", duration: 0, waveform: nil, source: nil, @@ -256,7 +257,8 @@ struct TimelineReplyView_Previews: PreviewProvider, TestablePreview { TimelineReplyView(placement: .timeline, timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"), eventID: "123", - eventContent: .message(.file(.init(body: "Some file", + eventContent: .message(.file(.init(filename: "file.txt", + caption: "Some file", source: nil, thumbnailSource: nil, contentType: nil))))), @@ -264,14 +266,16 @@ struct TimelineReplyView_Previews: PreviewProvider, TestablePreview { TimelineReplyView(placement: .timeline, timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"), eventID: "123", - eventContent: .message(.image(.init(body: "Some image", + eventContent: .message(.image(.init(filename: "image.jpg", + caption: "Some image", source: imageSource, thumbnailSource: imageSource))))), TimelineReplyView(placement: .timeline, timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"), eventID: "123", - eventContent: .message(.video(.init(body: "Some video", + eventContent: .message(.video(.init(filename: "video.mp4", + caption: "Some video", duration: 0, source: nil, thumbnailSource: imageSource))))), @@ -283,7 +287,8 @@ struct TimelineReplyView_Previews: PreviewProvider, TestablePreview { TimelineReplyView(placement: .timeline, timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"), eventID: "123", - eventContent: .message(.voice(.init(body: "Some voice message", + eventContent: .message(.voice(.init(filename: "voice-message.ogg", + caption: "Some voice message", duration: 0, waveform: nil, source: nil, diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift index 9b0213f25c..7d809e2cb2 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift @@ -355,7 +355,7 @@ private extension View { struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview { static let viewModel = TimelineViewModel.mock static let viewModelWithPins: TimelineViewModel = { - let roomProxy = JoinedRoomProxyMock(.init(name: "Preview Room", pinnedEventIDs: [""])) + let roomProxy = JoinedRoomProxyMock(.init(name: "Preview Room", pinnedEventIDs: ["pinned"])) return TimelineViewModel(roomProxy: roomProxy, focussedEventID: nil, timelineController: MockRoomTimelineController(), @@ -385,113 +385,6 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview .snapshotPreferences(delay: 2.0) } - // These always include a reply - static var threads: some View { - ScrollView { - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), - timestamp: "10:42", - isOutgoing: true, - isEditable: false, - canBeRepliedTo: true, - isThreaded: true, - sender: .init(id: "whoever"), - content: .init(body: "A long message that should be on multiple lines."), - replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"), - eventID: "123", - eventContent: .message(.text(.init(body: "Short"))))), - groupStyle: .single)) - - AudioRoomTimelineView(timelineItem: .init(id: .init(uniqueID: ""), - timestamp: "10:42", - isOutgoing: true, - isEditable: false, - canBeRepliedTo: true, - isThreaded: true, - sender: .init(id: ""), - content: .init(body: "audio.ogg", - duration: 100, - waveform: EstimatedWaveform.mockWaveform, - source: nil, - contentType: nil), - replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"), - eventID: "123", - eventContent: .message(.text(.init(body: "Short")))))) - - FileRoomTimelineView(timelineItem: .init(id: .init(uniqueID: ""), - timestamp: "10:42", - isOutgoing: false, - isEditable: false, - canBeRepliedTo: true, - isThreaded: true, - sender: .init(id: ""), - content: .init(body: "File", - source: nil, - thumbnailSource: nil, - contentType: nil), - replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"), - eventID: "123", - eventContent: .message(.text(.init(body: "Short")))))) - ImageRoomTimelineView(timelineItem: .init(id: .init(uniqueID: ""), - timestamp: "10:42", - isOutgoing: true, - isEditable: true, - canBeRepliedTo: true, - isThreaded: true, - sender: .init(id: ""), - content: .init(body: "Some image", source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), thumbnailSource: nil), - replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"), - eventID: "123", - eventContent: .message(.text(.init(body: "Short")))))) - LocationRoomTimelineView(timelineItem: .init(id: .random, - timestamp: "Now", - isOutgoing: false, - isEditable: false, - canBeRepliedTo: true, - isThreaded: true, - sender: .init(id: "Bob"), - content: .init(body: "Fallback geo uri description", - geoURI: .init(latitude: 41.902782, - longitude: 12.496366), - description: "Location description description description description description description description description"), - replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"), - eventID: "123", - eventContent: .message(.text(.init(body: "Short")))))) - LocationRoomTimelineView(timelineItem: .init(id: .random, - timestamp: "Now", - isOutgoing: false, - isEditable: false, - canBeRepliedTo: true, - isThreaded: true, - sender: .init(id: "Bob"), - content: .init(body: "Fallback geo uri description", - geoURI: .init(latitude: 41.902782, longitude: 12.496366), description: nil), - replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"), - eventID: "123", - eventContent: .message(.text(.init(body: "Short")))))) - - VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(uniqueID: ""), - timestamp: "10:42", - isOutgoing: true, - isEditable: false, - canBeRepliedTo: true, - isThreaded: true, - sender: .init(id: ""), - content: .init(body: "audio.ogg", - duration: 100, - waveform: EstimatedWaveform.mockWaveform, - source: nil, - contentType: nil), - replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"), - eventID: "123", - eventContent: .message(.text(.init(body: "Short"))))), - playerState: AudioPlayerState(id: .timelineItemIdentifier(.random), - title: L10n.commonVoiceMessage, - duration: 10, - waveform: EstimatedWaveform.mockWaveform)) - } - .environmentObject(viewModel.context) - } - static var mockTimeline: some View { ScrollView { VStack(alignment: .leading, spacing: 0) { @@ -502,7 +395,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview } .environmentObject(viewModel.context) } - + static var replies: some View { VStack(spacing: 0) { RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), @@ -533,7 +426,21 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview } .environmentObject(viewModel.context) } - + + static var threads: some View { + ScrollView { + MockTimelineContent(isThreaded: true) + } + .environmentObject(viewModel.context) + } + + static var pinned: some View { + ScrollView { + MockTimelineContent(isPinned: true) + } + .environmentObject(viewModelWithPins.context) + } + static var encryptionAuthenticity: some View { VStack(spacing: 0) { RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), @@ -588,7 +495,9 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview canBeRepliedTo: true, isThreaded: false, sender: .init(id: "Bob"), - content: .init(body: "Some other image", source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), thumbnailSource: nil), + content: .init(filename: "other.png", + source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), + thumbnailSource: nil), properties: RoomTimelineItemProperties(encryptionAuthenticity: .notGuaranteed(color: .gray)))) @@ -599,7 +508,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview canBeRepliedTo: true, isThreaded: true, sender: .init(id: ""), - content: .init(body: "audio.ogg", + content: .init(filename: "audio.ogg", duration: 100, waveform: EstimatedWaveform.mockWaveform, source: nil, @@ -612,96 +521,114 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview } .environmentObject(viewModel.context) } - - static var pinned: some View { - ScrollView { - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")), - timestamp: "10:42", - isOutgoing: true, - isEditable: false, - canBeRepliedTo: true, - isThreaded: false, - sender: .init(id: "whoever"), - content: .init(body: "A long message that should be on multiple lines."), - replyDetails: nil), - groupStyle: .single)) +} - AudioRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")), - timestamp: "10:42", - isOutgoing: true, - isEditable: false, - canBeRepliedTo: true, - isThreaded: false, - sender: .init(id: ""), - content: .init(body: "audio.ogg", - duration: 100, - waveform: EstimatedWaveform.mockWaveform, - source: nil, - contentType: nil), - replyDetails: nil)) - - FileRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")), - timestamp: "10:42", +private struct MockTimelineContent: View { + var isThreaded = false + var isPinned = false + + var body: some View { + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: makeItemIdentifier(), + timestamp: "10:42", + isOutgoing: true, + isEditable: false, + canBeRepliedTo: true, + isThreaded: isThreaded, + sender: .init(id: "whoever"), + content: .init(body: "A long message that should be on multiple lines."), + replyDetails: replyDetails), + groupStyle: .single)) + + AudioRoomTimelineView(timelineItem: .init(id: makeItemIdentifier(), + timestamp: "10:42", + isOutgoing: true, + isEditable: false, + canBeRepliedTo: true, + isThreaded: isThreaded, + sender: .init(id: ""), + content: .init(filename: "audio.ogg", + duration: 100, + waveform: EstimatedWaveform.mockWaveform, + source: nil, + contentType: nil), + replyDetails: replyDetails)) + + FileRoomTimelineView(timelineItem: .init(id: makeItemIdentifier(), + timestamp: "10:42", + isOutgoing: false, + isEditable: false, + canBeRepliedTo: true, + isThreaded: isThreaded, + sender: .init(id: ""), + content: .init(filename: "file.txt", + caption: "File", + source: nil, + thumbnailSource: nil, + contentType: nil), + replyDetails: replyDetails)) + + ImageRoomTimelineView(timelineItem: .init(id: makeItemIdentifier(), + timestamp: "10:42", + isOutgoing: true, + isEditable: true, + canBeRepliedTo: true, + isThreaded: isThreaded, + sender: .init(id: ""), + content: .init(filename: "image.jpg", + source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), + thumbnailSource: nil), + replyDetails: replyDetails)) + + LocationRoomTimelineView(timelineItem: .init(id: makeItemIdentifier(), + timestamp: "Now", isOutgoing: false, isEditable: false, canBeRepliedTo: true, - isThreaded: false, - sender: .init(id: ""), - content: .init(body: "File", - source: nil, - thumbnailSource: nil, - contentType: nil), - replyDetails: nil)) - ImageRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")), - timestamp: "10:42", - isOutgoing: true, - isEditable: true, - canBeRepliedTo: true, - isThreaded: false, - sender: .init(id: ""), - content: .init(body: "Some image", source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), thumbnailSource: nil), - replyDetails: nil)) - LocationRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")), - timestamp: "Now", - isOutgoing: false, - isEditable: false, - canBeRepliedTo: true, - isThreaded: false, - sender: .init(id: "Bob"), - content: .init(body: "Fallback geo uri description", - geoURI: .init(latitude: 41.902782, - longitude: 12.496366), - description: "Location description description description description description description description description"), - replyDetails: nil)) - LocationRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")), - timestamp: "Now", - isOutgoing: false, + isThreaded: isThreaded, + sender: .init(id: "Bob"), + content: .init(body: "Fallback geo uri description", + geoURI: .init(latitude: 41.902782, + longitude: 12.496366), + description: "Location description description description description description description description description"), + replyDetails: replyDetails)) + + LocationRoomTimelineView(timelineItem: .init(id: makeItemIdentifier(), + timestamp: "Now", + isOutgoing: false, + isEditable: false, + canBeRepliedTo: true, + isThreaded: isThreaded, + sender: .init(id: "Bob"), + content: .init(body: "Fallback geo uri description", + geoURI: .init(latitude: 41.902782, longitude: 12.496366), description: nil), + replyDetails: replyDetails)) + + VoiceMessageRoomTimelineView(timelineItem: .init(id: makeItemIdentifier(), + timestamp: "10:42", + isOutgoing: true, isEditable: false, canBeRepliedTo: true, - isThreaded: false, - sender: .init(id: "Bob"), - content: .init(body: "Fallback geo uri description", - geoURI: .init(latitude: 41.902782, longitude: 12.496366), description: nil), - replyDetails: nil)) - - VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "")), - timestamp: "10:42", - isOutgoing: true, - isEditable: false, - canBeRepliedTo: true, - isThreaded: false, - sender: .init(id: ""), - content: .init(body: "audio.ogg", - duration: 100, - waveform: EstimatedWaveform.mockWaveform, - source: nil, - contentType: nil), - replyDetails: nil), - playerState: AudioPlayerState(id: .timelineItemIdentifier(.random), - title: L10n.commonVoiceMessage, - duration: 10, - waveform: EstimatedWaveform.mockWaveform)) - } - .environmentObject(viewModelWithPins.context) + isThreaded: isThreaded, + sender: .init(id: ""), + content: .init(filename: "audio.ogg", + duration: 100, + waveform: EstimatedWaveform.mockWaveform, + source: nil, + contentType: nil), + replyDetails: replyDetails), + playerState: AudioPlayerState(id: .timelineItemIdentifier(.random), + title: L10n.commonVoiceMessage, + duration: 10, + waveform: EstimatedWaveform.mockWaveform)) + } + + func makeItemIdentifier() -> TimelineItemIdentifier { + isPinned ? .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "pinned")) : .random + } + + var replyDetails: TimelineItemReplyDetails? { + isThreaded ? .loaded(sender: .init(id: "", displayName: "Alice"), + eventID: "123", + eventContent: .message(.text(.init(body: "Short")))) : nil } } diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/AudioRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/AudioRoomTimelineView.swift index a116d81efe..59952d1185 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/AudioRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/AudioRoomTimelineView.swift @@ -41,6 +41,10 @@ struct AudioRoomTimelineView_Previews: PreviewProvider, TestablePreview { canBeRepliedTo: true, isThreaded: false, sender: .init(id: "Bob"), - content: .init(body: "audio.ogg", duration: 300, waveform: nil, source: nil, contentType: nil))) + content: .init(filename: "audio.ogg", + duration: 300, + waveform: nil, + source: nil, + contentType: nil))) } } diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/FileRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/FileRoomTimelineView.swift index faf03fcf13..a75a139b38 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/FileRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/FileRoomTimelineView.swift @@ -42,7 +42,10 @@ struct FileRoomTimelineView_Previews: PreviewProvider, TestablePreview { canBeRepliedTo: true, isThreaded: false, sender: .init(id: "Bob"), - content: .init(body: "document.pdf", source: nil, thumbnailSource: nil, contentType: nil))) + content: .init(filename: "document.pdf", + source: nil, + thumbnailSource: nil, + contentType: nil))) FileRoomTimelineView(timelineItem: FileRoomTimelineItem(id: .random, timestamp: "Now", @@ -51,7 +54,10 @@ struct FileRoomTimelineView_Previews: PreviewProvider, TestablePreview { canBeRepliedTo: true, isThreaded: false, sender: .init(id: "Bob"), - content: .init(body: "document.docx", source: nil, thumbnailSource: nil, contentType: nil))) + content: .init(filename: "document.docx", + source: nil, + thumbnailSource: nil, + contentType: nil))) FileRoomTimelineView(timelineItem: FileRoomTimelineItem(id: .random, timestamp: "Now", @@ -60,7 +66,10 @@ struct FileRoomTimelineView_Previews: PreviewProvider, TestablePreview { canBeRepliedTo: true, isThreaded: false, sender: .init(id: "Bob"), - content: .init(body: "document.txt", source: nil, thumbnailSource: nil, contentType: nil))) + content: .init(filename: "document.txt", + source: nil, + thumbnailSource: nil, + contentType: nil))) } } } diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift index c213ead505..9c8c2471a6 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift @@ -59,7 +59,9 @@ struct ImageRoomTimelineView_Previews: PreviewProvider, TestablePreview { canBeRepliedTo: true, isThreaded: false, sender: .init(id: "Bob"), - content: .init(body: "Some image", source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), thumbnailSource: nil))) + content: .init(filename: "image.jpg", + source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/jpg"), + thumbnailSource: nil))) ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .random, timestamp: "Now", @@ -68,7 +70,9 @@ struct ImageRoomTimelineView_Previews: PreviewProvider, TestablePreview { canBeRepliedTo: true, isThreaded: false, sender: .init(id: "Bob"), - content: .init(body: "Some other image", source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), thumbnailSource: nil))) + content: .init(filename: "other.png", + source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), + thumbnailSource: nil))) ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .random, timestamp: "Now", @@ -77,7 +81,7 @@ struct ImageRoomTimelineView_Previews: PreviewProvider, TestablePreview { canBeRepliedTo: true, isThreaded: false, sender: .init(id: "Bob"), - content: .init(body: "Blurhashed image", + content: .init(filename: "Blurhashed.jpg", source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/gif"), thumbnailSource: nil, aspectRatio: 0.7, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift index 23bc73d589..fd530ff938 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift @@ -70,7 +70,10 @@ struct VideoRoomTimelineView_Previews: PreviewProvider, TestablePreview { canBeRepliedTo: true, isThreaded: false, sender: .init(id: "Bob"), - content: .init(body: "Some video", duration: 21, source: nil, thumbnailSource: nil))) + content: .init(filename: "video.mp4", + duration: 21, + source: nil, + thumbnailSource: nil))) VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .random, timestamp: "Now", @@ -79,7 +82,10 @@ struct VideoRoomTimelineView_Previews: PreviewProvider, TestablePreview { canBeRepliedTo: true, isThreaded: false, sender: .init(id: "Bob"), - content: .init(body: "Some other video", duration: 22, source: nil, thumbnailSource: nil))) + content: .init(filename: "other.mp4", + duration: 22, + source: nil, + thumbnailSource: nil))) VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .random, timestamp: "Now", @@ -88,7 +94,12 @@ struct VideoRoomTimelineView_Previews: PreviewProvider, TestablePreview { canBeRepliedTo: true, isThreaded: false, sender: .init(id: "Bob"), - content: .init(body: "Blurhashed video", duration: 23, source: nil, thumbnailSource: nil, aspectRatio: 0.7, blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW"))) + content: .init(filename: "Blurhashed.mp4", + duration: 23, + source: nil, + thumbnailSource: nil, + aspectRatio: 0.7, + blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW"))) } } } diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 3ab6cfaac8..8ed91d96c4 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -929,8 +929,8 @@ extension ClientProxy: MediaLoaderProtocol { try await mediaLoader.loadMediaThumbnailForSource(source, width: width, height: height) } - func loadMediaFileForSource(_ source: MediaSourceProxy, body: String?) async throws -> MediaFileHandleProxy { - try await mediaLoader.loadMediaFileForSource(source, body: body) + func loadMediaFileForSource(_ source: MediaSourceProxy, filename: String?) async throws -> MediaFileHandleProxy { + try await mediaLoader.loadMediaFileForSource(source, filename: filename) } } diff --git a/ElementX/Sources/Services/Media/Provider/MediaLoader.swift b/ElementX/Sources/Services/Media/Provider/MediaLoader.swift index 427aeb77c5..fe4e3b2fcc 100644 --- a/ElementX/Sources/Services/Media/Provider/MediaLoader.swift +++ b/ElementX/Sources/Services/Media/Provider/MediaLoader.swift @@ -34,8 +34,8 @@ actor MediaLoader: MediaLoaderProtocol { } } - func loadMediaFileForSource(_ source: MediaSourceProxy, body: String?) async throws -> MediaFileHandleProxy { - let result = try await client.getMediaFile(mediaSource: source.underlyingSource, body: body, mimeType: source.mimeType ?? "application/octet-stream", useCache: true, tempDir: nil) + func loadMediaFileForSource(_ source: MediaSourceProxy, filename: String?) async throws -> MediaFileHandleProxy { + let result = try await client.getMediaFile(mediaSource: source.underlyingSource, body: filename, mimeType: source.mimeType ?? "application/octet-stream", useCache: true, tempDir: nil) return MediaFileHandleProxy(handle: result) } diff --git a/ElementX/Sources/Services/Media/Provider/MediaLoaderProtocol.swift b/ElementX/Sources/Services/Media/Provider/MediaLoaderProtocol.swift index a19ef95a81..7600f47358 100644 --- a/ElementX/Sources/Services/Media/Provider/MediaLoaderProtocol.swift +++ b/ElementX/Sources/Services/Media/Provider/MediaLoaderProtocol.swift @@ -13,5 +13,5 @@ protocol MediaLoaderProtocol { func loadMediaThumbnailForSource(_ source: MediaSourceProxy, width: UInt, height: UInt) async throws -> Data - func loadMediaFileForSource(_ source: MediaSourceProxy, body: String?) async throws -> MediaFileHandleProxy + func loadMediaFileForSource(_ source: MediaSourceProxy, filename: String?) async throws -> MediaFileHandleProxy } diff --git a/ElementX/Sources/Services/Media/Provider/MediaProvider.swift b/ElementX/Sources/Services/Media/Provider/MediaProvider.swift index 408e858748..a3c3ab7d28 100644 --- a/ElementX/Sources/Services/Media/Provider/MediaProvider.swift +++ b/ElementX/Sources/Services/Media/Provider/MediaProvider.swift @@ -117,9 +117,9 @@ struct MediaProvider: MediaProviderProtocol { // MARK: Files - func loadFileFromSource(_ source: MediaSourceProxy, body: String?) async -> Result { + func loadFileFromSource(_ source: MediaSourceProxy, filename: String?) async -> Result { do { - let file = try await mediaLoader.loadMediaFileForSource(source, body: body) + let file = try await mediaLoader.loadMediaFileForSource(source, filename: filename) return .success(file) } catch { MXLog.error("Failed retrieving file with error: \(error)") diff --git a/ElementX/Sources/Services/Media/Provider/MediaProviderProtocol.swift b/ElementX/Sources/Services/Media/Provider/MediaProviderProtocol.swift index bae831b446..6a59172eca 100644 --- a/ElementX/Sources/Services/Media/Provider/MediaProviderProtocol.swift +++ b/ElementX/Sources/Services/Media/Provider/MediaProviderProtocol.swift @@ -25,7 +25,7 @@ protocol MediaProviderProtocol { func loadThumbnailForSource(source: MediaSourceProxy, size: CGSize) async -> Result - func loadFileFromSource(_ source: MediaSourceProxy, body: String?) async -> Result + func loadFileFromSource(_ source: MediaSourceProxy, filename: String?) async -> Result } extension MediaProviderProtocol { @@ -38,6 +38,6 @@ extension MediaProviderProtocol { } func loadFileFromSource(_ source: MediaSourceProxy) async -> Result { - await loadFileFromSource(source, body: nil) + await loadFileFromSource(source, filename: nil) } } diff --git a/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift b/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift index 820e880b81..ff84de4355 100644 --- a/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift +++ b/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift @@ -257,7 +257,7 @@ enum RoomTimelineItemFixtures { canBeRepliedTo: true, isThreaded: false, sender: .init(id: ""), - content: .init(body: "video", + content: .init(filename: "video.mp4", duration: 100, source: .init(url: .picturesDirectory, mimeType: nil), thumbnailSource: .init(url: .picturesDirectory, mimeType: nil), @@ -272,7 +272,7 @@ enum RoomTimelineItemFixtures { canBeRepliedTo: true, isThreaded: false, sender: .init(id: ""), - content: .init(body: "image", + content: .init(filename: "image.jpg", source: .init(url: .picturesDirectory, mimeType: nil), thumbnailSource: nil, width: 5120, diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/AudioRoomTimelineItem.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/AudioRoomTimelineItem.swift index 6d99259b46..6b556dcbe0 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/AudioRoomTimelineItem.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/AudioRoomTimelineItem.swift @@ -23,7 +23,7 @@ struct AudioRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Equatable { var properties = RoomTimelineItemProperties() var body: String { - content.body + content.caption ?? content.filename } var contentType: EventBasedMessageTimelineItemContentType { diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/AudioRoomTimelineItemContent.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/AudioRoomTimelineItemContent.swift index 6353b619e1..4ca8d5c1f1 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/AudioRoomTimelineItemContent.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/AudioRoomTimelineItemContent.swift @@ -9,7 +9,11 @@ import UIKit import UniformTypeIdentifiers struct AudioRoomTimelineItemContent: Hashable { - let body: String + let filename: String + var caption: String? + var formattedCaption: AttributedString? + /// The original textual representation of the formatted caption directly from the event (usually HTML code) + var formattedCaptionHTMLString: String? let duration: TimeInterval let waveform: EstimatedWaveform? let source: MediaSourceProxy? diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/FileRoomTimelineItem.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/FileRoomTimelineItem.swift index 3b531a7e18..bb025ee776 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/FileRoomTimelineItem.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/FileRoomTimelineItem.swift @@ -26,7 +26,7 @@ struct FileRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Equatable { var properties = RoomTimelineItemProperties() var body: String { - content.body + content.caption ?? content.filename } var contentType: EventBasedMessageTimelineItemContentType { diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/FileRoomTimelineItemContent.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/FileRoomTimelineItemContent.swift index 9c0cfe9798..67f00ee1ba 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/FileRoomTimelineItemContent.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/FileRoomTimelineItemContent.swift @@ -9,7 +9,11 @@ import Foundation import UniformTypeIdentifiers struct FileRoomTimelineItemContent: Hashable { - let body: String + let filename: String + var caption: String? + var formattedCaption: AttributedString? + /// The original textual representation of the formatted caption directly from the event (usually HTML code) + var formattedCaptionHTMLString: String? let source: MediaSourceProxy? let thumbnailSource: MediaSourceProxy? let contentType: UTType? diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/ImageRoomTimelineItem.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/ImageRoomTimelineItem.swift index 759e9d4567..b556b1e187 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/ImageRoomTimelineItem.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/ImageRoomTimelineItem.swift @@ -25,7 +25,7 @@ struct ImageRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Equatable { var properties = RoomTimelineItemProperties() var body: String { - content.body + content.caption ?? content.filename } var contentType: EventBasedMessageTimelineItemContentType { diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/ImageRoomTimelineItemContent.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/ImageRoomTimelineItemContent.swift index 0ef2e27aa2..01eb841510 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/ImageRoomTimelineItemContent.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/ImageRoomTimelineItemContent.swift @@ -9,7 +9,11 @@ import Foundation import UniformTypeIdentifiers struct ImageRoomTimelineItemContent: Hashable { - let body: String + let filename: String + var caption: String? + var formattedCaption: AttributedString? + /// The original textual representation of the formatted caption directly from the event (usually HTML code) + var formattedCaptionHTMLString: String? let source: MediaSourceProxy let thumbnailSource: MediaSourceProxy? var width: CGFloat? diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VideoRoomTimelineItem.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VideoRoomTimelineItem.swift index 896d6a81ff..5247fd6cd2 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VideoRoomTimelineItem.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VideoRoomTimelineItem.swift @@ -25,7 +25,7 @@ struct VideoRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Equatable { var properties = RoomTimelineItemProperties() var body: String { - content.body + content.caption ?? content.filename } var contentType: EventBasedMessageTimelineItemContentType { diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VideoRoomTimelineItemContent.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VideoRoomTimelineItemContent.swift index 905008a6f3..0790970edf 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VideoRoomTimelineItemContent.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VideoRoomTimelineItemContent.swift @@ -9,7 +9,11 @@ import Foundation import UniformTypeIdentifiers struct VideoRoomTimelineItemContent: Hashable { - let body: String + let filename: String + var caption: String? + var formattedCaption: AttributedString? + /// The original textual representation of the formatted caption directly from the event (usually HTML code) + var formattedCaptionHTMLString: String? let duration: TimeInterval let source: MediaSourceProxy? let thumbnailSource: MediaSourceProxy? diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessageRoomTimelineItem.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessageRoomTimelineItem.swift index 22837d2f5e..d8e0769988 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessageRoomTimelineItem.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessageRoomTimelineItem.swift @@ -23,7 +23,7 @@ struct VoiceMessageRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Equa var properties = RoomTimelineItemProperties() var body: String { - content.body + content.caption ?? content.filename } var contentType: EventBasedMessageTimelineItemContentType { diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift index 1cde05ca25..06ca0a137c 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift @@ -63,7 +63,7 @@ struct VoiceMessageRoomTimelineView_Previews: PreviewProvider, TestablePreview { canBeRepliedTo: true, isThreaded: false, sender: .init(id: "Bob"), - content: .init(body: "audio.ogg", + content: .init(filename: "audio.ogg", duration: 300, waveform: EstimatedWaveform.mockWaveform, source: nil, diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift index 75c1cfdd3f..f43d5e4c83 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift @@ -490,12 +490,18 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { } private func buildAudioTimelineItemContent(_ messageContent: AudioMessageContent) -> AudioRoomTimelineItemContent { + let htmlCaption = messageContent.formattedCaption?.format == .html ? messageContent.formattedCaption?.body : nil + let formattedCaption = htmlCaption != nil ? attributedStringBuilder.fromHTML(htmlCaption) : attributedStringBuilder.fromPlain(messageContent.caption) + var waveform: EstimatedWaveform? if let audioWaveform = messageContent.audio?.waveform { waveform = EstimatedWaveform(data: audioWaveform) } - return AudioRoomTimelineItemContent(body: messageContent.body, + return AudioRoomTimelineItemContent(filename: messageContent.filename, + caption: messageContent.caption, + formattedCaption: formattedCaption, + formattedCaptionHTMLString: htmlCaption, duration: messageContent.audio?.duration ?? 0, waveform: waveform, source: MediaSourceProxy(source: messageContent.source, mimeType: messageContent.info?.mimetype), @@ -503,6 +509,9 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { } private func buildImageTimelineItemContent(_ messageContent: ImageMessageContent) -> ImageRoomTimelineItemContent { + let htmlCaption = messageContent.formattedCaption?.format == .html ? messageContent.formattedCaption?.body : nil + let formattedCaption = htmlCaption != nil ? attributedStringBuilder.fromHTML(htmlCaption) : attributedStringBuilder.fromPlain(messageContent.caption) + let thumbnailSource = messageContent.info?.thumbnailSource.map { MediaSourceProxy(source: $0, mimeType: messageContent.info?.thumbnailInfo?.mimetype) } let width = messageContent.info?.width.map(CGFloat.init) let height = messageContent.info?.height.map(CGFloat.init) @@ -512,7 +521,10 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { aspectRatio = width / height } - return .init(body: messageContent.body, + return .init(filename: messageContent.filename, + caption: messageContent.caption, + formattedCaption: formattedCaption, + formattedCaptionHTMLString: htmlCaption, source: MediaSourceProxy(source: messageContent.source, mimeType: messageContent.info?.mimetype), thumbnailSource: thumbnailSource, width: width, @@ -523,6 +535,9 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { } private func buildVideoTimelineItemContent(_ messageContent: VideoMessageContent) -> VideoRoomTimelineItemContent { + let htmlCaption = messageContent.formattedCaption?.format == .html ? messageContent.formattedCaption?.body : nil + let formattedCaption = htmlCaption != nil ? attributedStringBuilder.fromHTML(htmlCaption) : attributedStringBuilder.fromPlain(messageContent.caption) + let thumbnailSource = messageContent.info?.thumbnailSource.map { MediaSourceProxy(source: $0, mimeType: messageContent.info?.thumbnailInfo?.mimetype) } let width = messageContent.info?.width.map(CGFloat.init) let height = messageContent.info?.height.map(CGFloat.init) @@ -532,7 +547,10 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { aspectRatio = width / height } - return .init(body: messageContent.body, + return .init(filename: messageContent.filename, + caption: messageContent.caption, + formattedCaption: formattedCaption, + formattedCaptionHTMLString: htmlCaption, duration: messageContent.info?.duration ?? 0, source: MediaSourceProxy(source: messageContent.source, mimeType: messageContent.info?.mimetype), thumbnailSource: thumbnailSource, @@ -550,9 +568,15 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { } private func buildFileTimelineItemContent(_ messageContent: FileMessageContent) -> FileRoomTimelineItemContent { + let htmlCaption = messageContent.formattedCaption?.format == .html ? messageContent.formattedCaption?.body : nil + let formattedCaption = htmlCaption != nil ? attributedStringBuilder.fromHTML(htmlCaption) : attributedStringBuilder.fromPlain(messageContent.caption) + let thumbnailSource = messageContent.info?.thumbnailSource.map { MediaSourceProxy(source: $0, mimeType: messageContent.info?.thumbnailInfo?.mimetype) } - return .init(body: messageContent.body, + return .init(filename: messageContent.filename, + caption: messageContent.caption, + formattedCaption: formattedCaption, + formattedCaptionHTMLString: htmlCaption, source: MediaSourceProxy(source: messageContent.source, mimeType: messageContent.info?.mimetype), thumbnailSource: thumbnailSource, contentType: UTType(mimeType: messageContent.info?.mimetype, fallbackFilename: messageContent.body)) diff --git a/ElementX/Sources/Services/VoiceMessage/VoiceMessageMediaManager.swift b/ElementX/Sources/Services/VoiceMessage/VoiceMessageMediaManager.swift index e3ae2931cc..b1606ef486 100644 --- a/ElementX/Sources/Services/VoiceMessage/VoiceMessageMediaManager.swift +++ b/ElementX/Sources/Services/VoiceMessage/VoiceMessageMediaManager.swift @@ -51,7 +51,7 @@ class VoiceMessageMediaManager: VoiceMessageMediaManagerProtocol { } // Otherwise, load the file from source - guard case .success(let fileHandle) = await mediaProvider.loadFileFromSource(source, body: body) else { + guard case .success(let fileHandle) = await mediaProvider.loadFileFromSource(source, filename: body) else { throw MediaProviderError.failedRetrievingFile } diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-en-GB.Replying-in-thread.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-en-GB.Replying-in-thread.png index 89a898ec33..2773e12577 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-en-GB.Replying-in-thread.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-en-GB.Replying-in-thread.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b0e4caaf122d984b137f3d7bfebf701aa63def993cf2681ed06ddcdf4805af4 -size 197604 +oid sha256:6a034b807916e062e25c5fb59ecd09a47d0e1c7c822534b35e992fd9c839ab07 +size 196481 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-en-GB.Replying.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-en-GB.Replying.png index 89a898ec33..2773e12577 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-en-GB.Replying.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-en-GB.Replying.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b0e4caaf122d984b137f3d7bfebf701aa63def993cf2681ed06ddcdf4805af4 -size 197604 +oid sha256:6a034b807916e062e25c5fb59ecd09a47d0e1c7c822534b35e992fd9c839ab07 +size 196481 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-pseudo.Replying-in-thread.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-pseudo.Replying-in-thread.png index d7d11181b2..26e53c7a36 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-pseudo.Replying-in-thread.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-pseudo.Replying-in-thread.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f54b2d9bafaf2e9f2fd304dfa8e1956d079a7280e9075d9ef1f1b3005314ae3a -size 200745 +oid sha256:ffe1ceda422b0bccc1e587036ea047bebb4ee4643f54bdf25ff2b3b530b6f7e2 +size 199662 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-pseudo.Replying.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-pseudo.Replying.png index d7d11181b2..26e53c7a36 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-pseudo.Replying.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-pseudo.Replying.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f54b2d9bafaf2e9f2fd304dfa8e1956d079a7280e9075d9ef1f1b3005314ae3a -size 200745 +oid sha256:ffe1ceda422b0bccc1e587036ea047bebb4ee4643f54bdf25ff2b3b530b6f7e2 +size 199662 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-en-GB.Replying-in-thread.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-en-GB.Replying-in-thread.png index 3e6c0bba4f..e58f1c116b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-en-GB.Replying-in-thread.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-en-GB.Replying-in-thread.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d81827ae7b6798e844365ed7f0971d56df662cded25c03a300cf8a817afdbf51 -size 136742 +oid sha256:91bef32e5248f8084995b237b671cea005ce0eb9253a325165dd066a43934bfa +size 135848 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-en-GB.Replying.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-en-GB.Replying.png index 3e6c0bba4f..e58f1c116b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-en-GB.Replying.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-en-GB.Replying.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d81827ae7b6798e844365ed7f0971d56df662cded25c03a300cf8a817afdbf51 -size 136742 +oid sha256:91bef32e5248f8084995b237b671cea005ce0eb9253a325165dd066a43934bfa +size 135848 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-pseudo.Replying-in-thread.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-pseudo.Replying-in-thread.png index b0b67401d5..39ebe44626 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-pseudo.Replying-in-thread.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-pseudo.Replying-in-thread.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f16f475a1f4fd5e3c2a00e294103721df1b8bd38f69ccac9f4b295efac0e8af5 -size 139891 +oid sha256:c5fd3ee0cc6bb3cf124cc66877d87cde800a3da7c10f8e26d6e19d884f4ca40e +size 139138 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-pseudo.Replying.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-pseudo.Replying.png index b0b67401d5..39ebe44626 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-pseudo.Replying.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-pseudo.Replying.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f16f475a1f4fd5e3c2a00e294103721df1b8bd38f69ccac9f4b295efac0e8af5 -size 139891 +oid sha256:c5fd3ee0cc6bb3cf124cc66877d87cde800a3da7c10f8e26d6e19d884f4ca40e +size 139138 diff --git a/UnitTests/Sources/LoggingTests.swift b/UnitTests/Sources/LoggingTests.swift index 66e069fab1..9b22fd2660 100644 --- a/UnitTests/Sources/LoggingTests.swift +++ b/UnitTests/Sources/LoggingTests.swift @@ -149,7 +149,10 @@ class LoggingTests: XCTestCase { canBeRepliedTo: true, isThreaded: false, sender: .init(id: "sender"), - content: .init(body: "ImageString", source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/gif"), thumbnailSource: nil)) + content: .init(filename: "ImageString", + caption: "ImageString", + source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/gif"), + thumbnailSource: nil)) let videoMessage = VideoRoomTimelineItem(id: .random, timestamp: "", isOutgoing: false, @@ -157,7 +160,11 @@ class LoggingTests: XCTestCase { canBeRepliedTo: true, isThreaded: false, sender: .init(id: "sender"), - content: .init(body: "VideoString", duration: 0, source: nil, thumbnailSource: nil)) + content: .init(filename: "VideoString", + caption: "VideoString", + duration: 0, + source: nil, + thumbnailSource: nil)) let fileMessage = FileRoomTimelineItem(id: .random, timestamp: "", isOutgoing: false, @@ -165,7 +172,11 @@ class LoggingTests: XCTestCase { canBeRepliedTo: true, isThreaded: false, sender: .init(id: "sender"), - content: .init(body: "FileString", source: nil, thumbnailSource: nil, contentType: nil)) + content: .init(filename: "FileString", + caption: "FileString", + source: nil, + thumbnailSource: nil, + contentType: nil)) // When logging that value MXLog.configure(currentTarget: "tests", filePrefix: nil, logLevel: .info) diff --git a/UnitTests/Sources/VoiceMessageMediaManagerTests.swift b/UnitTests/Sources/VoiceMessageMediaManagerTests.swift index 8d8ed12812..7a85f15dc4 100644 --- a/UnitTests/Sources/VoiceMessageMediaManagerTests.swift +++ b/UnitTests/Sources/VoiceMessageMediaManagerTests.swift @@ -50,7 +50,7 @@ class VoiceMessageMediaManagerTests: XCTestCase { voiceMessageCache.fileURLForReturnValue = nil let mediaSource = MediaSourceProxy(url: someURL, mimeType: "audio/ogg; codecs=opus") - mediaProvider.loadFileFromSourceBodyReturnValue = .success(MediaFileHandleProxy.unmanaged(url: loadedFile)) + mediaProvider.loadFileFromSourceFilenameReturnValue = .success(MediaFileHandleProxy.unmanaged(url: loadedFile)) voiceMessageCache.cacheMediaSourceUsingMoveReturnValue = .success(cachedConvertedFileURL) voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, @@ -103,7 +103,7 @@ class VoiceMessageMediaManagerTests: XCTestCase { // Check if the file is not already present in cache voiceMessageCache.fileURLForReturnValue = nil let mediaSource = MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType) - mediaProvider.loadFileFromSourceBodyReturnValue = .success(MediaFileHandleProxy.unmanaged(url: loadedFile)) + mediaProvider.loadFileFromSourceFilenameReturnValue = .success(MediaFileHandleProxy.unmanaged(url: loadedFile)) let audioConverter = AudioConverterMock() voiceMessageCache.cacheMediaSourceUsingMoveReturnValue = .success(cachedConvertedFileURL) voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, @@ -139,7 +139,7 @@ class VoiceMessageMediaManagerTests: XCTestCase { } let audioConverter = AudioConverterMock() - mediaProvider.loadFileFromSourceBodyReturnValue = .success(MediaFileHandleProxy.unmanaged(url: loadedFile)) + mediaProvider.loadFileFromSourceFilenameReturnValue = .success(MediaFileHandleProxy.unmanaged(url: loadedFile)) voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, voiceMessageCache: voiceMessageCache, From a7f7fde27bf29a56fede7a49f4d53e714ac13207 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Tue, 8 Oct 2024 11:15:11 +0100 Subject: [PATCH 036/114] Add new emoji from iOS 17.4 to the reaction picker. (#3376) --- ElementX.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- .../PreviewTests/test_emojiPickerScreen-iPad-en-GB.Screen.png | 4 ++-- .../test_emojiPickerScreen-iPad-pseudo.Screen.png | 4 ++-- project.yml | 3 ++- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index df1d323860..7db0e5a673 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7810,7 +7810,7 @@ repositoryURL = "https://github.com/matrix-org/emojibase-bindings"; requirement = { kind = upToNextMinorVersion; - minimumVersion = 1.0.0; + minimumVersion = 1.3.3; }; }; 96495DD8554E2F39D3954354 /* XCRemoteSwiftPackageReference "posthog-ios" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 864c38bde3..a1da9e0494 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -59,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/matrix-org/emojibase-bindings", "state" : { - "revision" : "6ca06fefac9329fece851a2a4df7979b1699970a", - "version" : "1.0.5" + "revision" : "d4682a2ad5e68cfd2f41544c3c6c970b4d524bd1", + "version" : "1.3.3" } }, { diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_emojiPickerScreen-iPad-en-GB.Screen.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_emojiPickerScreen-iPad-en-GB.Screen.png index 58df3e323f..d86c93fce4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_emojiPickerScreen-iPad-en-GB.Screen.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_emojiPickerScreen-iPad-en-GB.Screen.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d0792296479dd1e54348bd980f10469dd8b1409cc0a02e2ae3fcbf349a019482 -size 984410 +oid sha256:d2b48d32a6432c287245037691aa0f576e3ca63468656bf96c73bbc408a45592 +size 983108 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_emojiPickerScreen-iPad-pseudo.Screen.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_emojiPickerScreen-iPad-pseudo.Screen.png index 5989d9b67d..42a40ca7d0 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_emojiPickerScreen-iPad-pseudo.Screen.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_emojiPickerScreen-iPad-pseudo.Screen.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8023ece990f28365497bd0fea7a1285e0cc70720bc717271051fc68f8c8588b0 -size 987899 +oid sha256:12609820bc5591c07fc2c8c2701906199f7e083d278b32e87ae1793f1c338728 +size 986590 diff --git a/project.yml b/project.yml index d0c6ff5b5a..13aba9c4d0 100644 --- a/project.yml +++ b/project.yml @@ -72,7 +72,8 @@ packages: # path: ../matrix-analytics-events Emojibase: url: https://github.com/matrix-org/emojibase-bindings - minorVersion: 1.0.0 + minorVersion: 1.3.3 + # path: ../emojibase-bindings SwiftOGG: url: https://github.com/element-hq/swift-ogg minorVersion: 0.0.3 From 798916c935536dc7ea34653735b252fa29113b08 Mon Sep 17 00:00:00 2001 From: Mauro <34335419+Velin92@users.noreply.github.com> Date: Tue, 8 Oct 2024 12:23:51 +0200 Subject: [PATCH 037/114] update sdk (#3377) --- ElementX.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- project.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 7db0e5a673..587dcef019 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7794,7 +7794,7 @@ repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.0.55; + version = 1.0.56; }; }; 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index a1da9e0494..250404a6ff 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/matrix-rust-components-swift", "state" : { - "revision" : "8ee63edc76bccd12c17a22eaf4eddae69e5f1303", - "version" : "1.0.55" + "revision" : "1366154c2e601179514be93e2fca48969c4f2ed8", + "version" : "1.0.56" } }, { diff --git a/project.yml b/project.yml index 13aba9c4d0..d08100fe37 100644 --- a/project.yml +++ b/project.yml @@ -60,7 +60,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/element-hq/matrix-rust-components-swift - exactVersion: 1.0.55 + exactVersion: 1.0.56 # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios From c087ece3bb632bc9f7be9d58fbfa952c58106111 Mon Sep 17 00:00:00 2001 From: Element CI Date: Tue, 8 Oct 2024 03:42:33 -0700 Subject: [PATCH 038/114] Prepare next release --- CHANGES.md | 53 ++++++++++++++++++++++++++++++ ElementX.xcodeproj/project.pbxproj | 4 +-- project.yml | 2 +- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a126a9f5aa..206778f122 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,56 @@ +## Changes in 1.8.5 (2024-10-08) + +### What's Changed + +✨ Features +* Display a warning when a user's pinned identity changes by @stefanceriu in https://github.com/element-hq/element-x-ios/pull/3368 + +🙌 Improvements +* Add detection for latest devices. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3327 +* Configure the AuthenticationService later now that we have 2 flows on the start screen. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3316 +* Selecting a server that doesn't support login now fails instead of letting you continue to a failure later. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3342 +* Add new emoji from iOS 17.4 to the reaction picker. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3376 + +🐛 Bugfixes +* Use a plain view for reactions instead of a TabView. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3328 +* Upgrade Kingfisher to fix a bug that prevented GIFs from being tapped. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3326 +* Make sure the room header takes up as much space as possible (to hide the back button). by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3335 +* Have ElementCall always default to the speaker; prevent the lock button from ending the call by @stefanceriu in https://github.com/element-hq/element-x-ios/pull/3338 +* Allow focusing the different avatars making up a DM details cluster separately. by @stefanceriu in https://github.com/element-hq/element-x-ios/pull/3341 +* Disable auto correction when running on the Mac by @stefanceriu in https://github.com/element-hq/element-x-ios/pull/3364 + +🗣 Translations +* Translations update by @RiotRobot in https://github.com/element-hq/element-x-ios/pull/3347 +* Translations update by @RiotRobot in https://github.com/element-hq/element-x-ios/pull/3371 + +🧱 Build +* Start fixing flakey tests ❄️ by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3329 +* Integration test runner switch by @stefanceriu in https://github.com/element-hq/element-x-ios/pull/3343 +* Switch UI tests back to the perf-only runner. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3349 + +🚧 In development 🚧 +* Add developer option to hide media in the timeline. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3366 + +Others +* Integration test improvements by @stefanceriu in https://github.com/element-hq/element-x-ios/pull/3330 +* crypto: rename invisible crypto flag to deviceIsolationMode by @BillCarsonFr in https://github.com/element-hq/element-x-ios/pull/3331 +* chore(deps): update dependency fastlane to v2.223.0 by @renovate in https://github.com/element-hq/element-x-ios/pull/3337 +* Log any failures when creating a call widget. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3339 +* chore(deps): update dependency fastlane to v2.223.1 by @renovate in https://github.com/element-hq/element-x-ios/pull/3340 +* Tracing and integration test tweaks by @stefanceriu in https://github.com/element-hq/element-x-ios/pull/3336 +* Remove message pinning FF by @Velin92 in https://github.com/element-hq/element-x-ios/pull/3318 +* Move the core logic in LoginScreenCoordinator into the ViewModel. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3348 +* Bump the RustSDK to v1.0.53: adopt latest record based timeline item APIs by @stefanceriu in https://github.com/element-hq/element-x-ios/pull/3356 +* use element-hq RTE version by @Velin92 in https://github.com/element-hq/element-x-ios/pull/3360 +* Hide timeline media preparation by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3361 +* chore(deps): update dependency fastlane to v2.224.0 by @renovate in https://github.com/element-hq/element-x-ios/pull/3370 +* Record a missing snapshot. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3374 +* Update the SDK and use media `filename` and `caption` internally. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3375 +* update sdk by @Velin92 in https://github.com/element-hq/element-x-ios/pull/3377 + + +**Full Changelog**: https://github.com/element-hq/element-x-ios/compare/1.8.4...1.8.5 + ## Changes in 1.8.4 (2024-09-24) ### What's Changed diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 587dcef019..4c605dca35 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7433,7 +7433,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.8.5; + MARKETING_VERSION = 1.8.6; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCTION_APP_NAME = Element; @@ -7510,7 +7510,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.8.5; + MARKETING_VERSION = 1.8.6; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; diff --git a/project.yml b/project.yml index d08100fe37..b233b61c77 100644 --- a/project.yml +++ b/project.yml @@ -41,7 +41,7 @@ settings: APP_GROUP_IDENTIFIER: group.$(BASE_APP_GROUP_IDENTIFIER) APP_NAME: ElementX KEYCHAIN_ACCESS_GROUP_IDENTIFIER: "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)" - MARKETING_VERSION: 1.8.5 + MARKETING_VERSION: 1.8.6 CURRENT_PROJECT_VERSION: 1 SUPPORTS_MACCATALYST: false From cf81072ff375ca1286eee6a890cb950c01db8f32 Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 8 Oct 2024 16:25:34 +0200 Subject: [PATCH 039/114] crypto: Configure decryption trustRequirement based on config flag (#3358) * crypto: Configure decryption trustRequirement based on config flag * quick review --- ElementX/Sources/Other/Extensions/ClientBuilder.swift | 8 ++++++-- .../View/DeveloperOptionsScreen.swift | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ElementX/Sources/Other/Extensions/ClientBuilder.swift b/ElementX/Sources/Other/Extensions/ClientBuilder.swift index 957eb852d7..6abce9a3b7 100644 --- a/ElementX/Sources/Other/Extensions/ClientBuilder.swift +++ b/ElementX/Sources/Other/Extensions/ClientBuilder.swift @@ -35,9 +35,13 @@ extension ClientBuilder { .autoEnableBackups(autoEnableBackups: true) if enableOnlySignedDeviceIsolationMode { - builder = builder.roomKeyRecipientStrategy(strategy: CollectStrategy.identityBasedStrategy) + builder = builder + .roomKeyRecipientStrategy(strategy: .identityBasedStrategy) + .roomDecryptionTrustRequirement(trustRequirement: .crossSignedOrLegacy) } else { - builder = builder.roomKeyRecipientStrategy(strategy: .deviceBasedStrategy(onlyAllowTrustedDevices: false, errorOnVerifiedUserProblem: true)) + builder = builder + .roomKeyRecipientStrategy(strategy: .deviceBasedStrategy(onlyAllowTrustedDevices: false, errorOnVerifiedUserProblem: true)) + .roomDecryptionTrustRequirement(trustRequirement: .untrusted) } } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift index 683b0a1f1b..878efcb09e 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -53,7 +53,7 @@ struct DeveloperOptionsScreen: View { Section { Toggle(isOn: $context.enableOnlySignedDeviceIsolationMode) { - Text("Exclude not secure devices when sending/receiving messages") + Text("Exclude insecure devices when sending/receiving messages") Text("Requires app reboot") } } header: { From 5c2b107022484791f681a295a4eb3ca08fbbf410 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:51:55 +0100 Subject: [PATCH 040/114] Fix an authentication UI test snapshot. --- .../authenticationFlow-1-iPad-10th-generation-en-GB.UI.png | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPad-10th-generation-en-GB.UI.png index c65a1a76d9..2a869ab2fd 100644 --- a/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b059e871878224c54b4b69d59f95394cf73c5d86e0f83ea030808efb9b48767 -size 88666 +oid sha256:1e6ff8c6a27cc995574326c6ec7952906170abbda18dad5721388fda88595fb3 +size 167621 From 2194908ee58935e0540bfd1cae1d52b1c012dc6c Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Wed, 9 Oct 2024 17:54:01 +0100 Subject: [PATCH 041/114] Add a subtitle to the QR login instructions. (#3386) --- .../Localizations/en.lproj/Localizable.strings | 1 + ElementX/Sources/Generated/Strings.swift | 2 ++ .../View/QRCodeLoginScreen.swift | 15 +++++++++++---- .../test_qRCodeLoginScreen-iPad-en-GB.Initial.png | 4 ++-- ...test_qRCodeLoginScreen-iPad-pseudo.Initial.png | 4 ++-- ..._qRCodeLoginScreen-iPhone-16-en-GB.Initial.png | 4 ++-- ...qRCodeLoginScreen-iPhone-16-pseudo.Initial.png | 4 ++-- 7 files changed, 22 insertions(+), 12 deletions(-) diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index f2bc97b9a4..6416ef7e17 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -588,6 +588,7 @@ "screen_qr_code_login_initial_state_item_3" = "Select %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Link new device”"; "screen_qr_code_login_initial_state_item_4" = "Scan the QR code with this device"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Open %1$@ on another device to get the QR code"; "screen_qr_code_login_invalid_scan_state_description" = "Use the QR code shown on the other device."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Wrong QR code"; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 36f6866c76..f09ba5be42 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -1395,6 +1395,8 @@ internal enum L10n { internal static var screenQrCodeLoginInitialStateItem3Action: String { return L10n.tr("Localizable", "screen_qr_code_login_initial_state_item_3_action") } /// Scan the QR code with this device internal static var screenQrCodeLoginInitialStateItem4: String { return L10n.tr("Localizable", "screen_qr_code_login_initial_state_item_4") } + /// Only available if your account provider supports it. + internal static var screenQrCodeLoginInitialStateSubtitle: String { return L10n.tr("Localizable", "screen_qr_code_login_initial_state_subtitle") } /// Open %1$@ on another device to get the QR code internal static func screenQrCodeLoginInitialStateTitle(_ p1: Any) -> String { return L10n.tr("Localizable", "screen_qr_code_login_initial_state_title", String(describing: p1)) diff --git a/ElementX/Sources/Screens/QRCodeLoginScreen/View/QRCodeLoginScreen.swift b/ElementX/Sources/Screens/QRCodeLoginScreen/View/QRCodeLoginScreen.swift index a0a9a0b664..e0349cab90 100644 --- a/ElementX/Sources/Screens/QRCodeLoginScreen/View/QRCodeLoginScreen.swift +++ b/ElementX/Sources/Screens/QRCodeLoginScreen/View/QRCodeLoginScreen.swift @@ -43,10 +43,17 @@ struct QRCodeLoginScreen: View { VStack(spacing: 16) { HeroImage(icon: \.computer, style: .subtle) - Text(L10n.screenQrCodeLoginInitialStateTitle(InfoPlistReader.main.productionAppName)) - .foregroundColor(.compound.textPrimary) - .font(.compound.headingMDBold) - .multilineTextAlignment(.center) + VStack(spacing: 8) { + Text(L10n.screenQrCodeLoginInitialStateTitle(InfoPlistReader.main.productionAppName)) + .foregroundColor(.compound.textPrimary) + .font(.compound.headingMDBold) + .multilineTextAlignment(.center) + + Text(L10n.screenQrCodeLoginInitialStateSubtitle) + .font(.compound.bodyMD) + .multilineTextAlignment(.center) + .foregroundColor(.compound.textSecondary) + } } .padding(.horizontal, 24) diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Initial.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Initial.png index 9c48c54549..c0721c9204 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Initial.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Initial.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5943061bd68ad5618e722cf2f6d58535e7af0095a480973c80e6dcf9d9b77959 -size 135762 +oid sha256:8eede65da7fcb800583f0f73a595cadecdb233f3185f7e272797b63e39f25df8 +size 146499 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Initial.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Initial.png index 02795455df..a810311f22 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Initial.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Initial.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d0ec013121390539a9155f4e56685332054dd5b941ff28690f7846ae76c4cb75 -size 163822 +oid sha256:695b6d2b5859fb05242e2c6450f69d5997f113ddd0346c12df21bb11d2447f96 +size 180110 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Initial.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Initial.png index 8125801fb4..dd30f177ea 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Initial.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Initial.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c418665989dab2f2a2ccc3ccce1293dc2da665f2bf14a34f931da866e4cc11db -size 83528 +oid sha256:bf8cc70bb5b9a1a79ca0743de1ff11521aa1ae81af1730a6d4988f77e14fe38c +size 92959 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Initial.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Initial.png index eee39bbea8..5d6b91504a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Initial.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Initial.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:73f83bfca996f596775273e3ee56558e093674e98ba37ce0006c04c82d1a5b0d -size 116625 +oid sha256:b9334283a4a16c6809f6e692e77aa989ccce35a72eac319e5e28497f2e81fef4 +size 133735 From 429ce4fe615f28582baaff9d1ff00c7e23bab486 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Thu, 10 Oct 2024 08:38:40 +0100 Subject: [PATCH 042/114] Ask the iPad to reveal the keyboard in UI Tests when it's hidden. (#3389) --- .../Sources/Other/Extensions/XCUIElement.swift | 18 +++++++++++++++++- UITests/Sources/AppLockSetupUITests.swift | 6 ++++-- .../AuthenticationFlowCoordinatorUITests.swift | 12 ++++++------ UITests/Sources/BugReportUITests.swift | 4 ++-- .../Sources/RoomMembersListScreenUITests.swift | 4 ++-- UITests/Sources/ServerSelectionUITests.swift | 2 +- UITests/Sources/StartChatScreenUITests.swift | 4 ++-- 7 files changed, 34 insertions(+), 16 deletions(-) diff --git a/ElementX/Sources/Other/Extensions/XCUIElement.swift b/ElementX/Sources/Other/Extensions/XCUIElement.swift index 83c26a615f..4d7436a1f1 100644 --- a/ElementX/Sources/Other/Extensions/XCUIElement.swift +++ b/ElementX/Sources/Other/Extensions/XCUIElement.swift @@ -8,9 +8,11 @@ import XCTest extension XCUIElement { - func clearAndTypeText(_ text: String) { + func clearAndTypeText(_ text: String, app: XCUIApplication) { tapCenter() + app.showKeyboardIfNeeded() + guard let currentValue = value as? String else { XCTFail("Tried to clear and type text into a non string value") return @@ -29,3 +31,17 @@ extension XCUIElement { coordinate.tap() } } + +extension XCUIApplication { + /// Ensures the software keyboard is shown on an iPad when a text field is focussed. + /// + /// Note: Whilst this could be added on XCUIElement to more closely tie it to a text field, it requires the + /// app instance anyway, and some of our tests assert that a default focus has been set on the text field, + /// so having a method that would set the focus and show the keyboard isn't always desirable. + func showKeyboardIfNeeded() { + if UIDevice.current.userInterfaceIdiom == .pad, keyboards.count == 0 { + buttons["Keyboard"].tap() + buttons["Show Keyboard"].tap() + } + } +} diff --git a/UITests/Sources/AppLockSetupUITests.swift b/UITests/Sources/AppLockSetupUITests.swift index 0911636358..5c2660af90 100644 --- a/UITests/Sources/AppLockSetupUITests.swift +++ b/UITests/Sources/AppLockSetupUITests.swift @@ -119,6 +119,8 @@ class AppLockSetupUITests: XCTestCase { func testCancel() async throws { app = Application.launch(.appLockSetupFlowUnlock) + app.showKeyboardIfNeeded() // The secure text field is focussed automatically + // Create PIN screen. try await app.assertScreenshot(.appLockSetupFlowUnlock) @@ -134,13 +136,13 @@ class AppLockSetupUITests: XCTestCase { let textField = app.secureTextFields[A11yIdentifiers.appLockSetupPINScreen.textField] XCTAssert(textField.waitForExistence(timeout: 10)) - textField.clearAndTypeText("2023") + textField.clearAndTypeText("2023", app: app) } private func enterDifferentPIN() { let textField = app.secureTextFields[A11yIdentifiers.appLockSetupPINScreen.textField] XCTAssert(textField.waitForExistence(timeout: 10)) - textField.clearAndTypeText("2233") + textField.clearAndTypeText("2233", app: app) } } diff --git a/UITests/Sources/AuthenticationFlowCoordinatorUITests.swift b/UITests/Sources/AuthenticationFlowCoordinatorUITests.swift index 0e38601c4b..40ff6d82c9 100644 --- a/UITests/Sources/AuthenticationFlowCoordinatorUITests.swift +++ b/UITests/Sources/AuthenticationFlowCoordinatorUITests.swift @@ -24,8 +24,8 @@ class AuthenticationFlowCoordinatorUITests: XCTestCase { XCTAssertTrue(continueButton.waitForExistence(timeout: 2.0)) // Login Screen: Enter valid credentials - app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("alice\n") - app.secureTextFields[A11yIdentifiers.loginScreen.password].clearAndTypeText("12345678") + app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("alice\n", app: app) + app.secureTextFields[A11yIdentifiers.loginScreen.password].clearAndTypeText("12345678", app: app) try await app.assertScreenshot(.authenticationFlow) @@ -48,8 +48,8 @@ class AuthenticationFlowCoordinatorUITests: XCTestCase { XCTAssertTrue(continueButton.waitForExistence(timeout: 2.0)) // Login Screen: Enter invalid credentials - app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("alice") - app.secureTextFields[A11yIdentifiers.loginScreen.password].clearAndTypeText("87654321") + app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("alice", app: app) + app.secureTextFields[A11yIdentifiers.loginScreen.password].clearAndTypeText("87654321", app: app) // Login Screen: Tap continue XCTAssertTrue(continueButton.isEnabled) @@ -74,7 +74,7 @@ class AuthenticationFlowCoordinatorUITests: XCTestCase { XCTAssertTrue(continueButton.waitForExistence(timeout: 2.0)) // When entering a username on a homeserver with an unsupported flow. - app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("@test:server.net\n") + app.textFields[A11yIdentifiers.loginScreen.emailUsername].clearAndTypeText("@test:server.net\n", app: app) // Then the screen should not allow login to continue. try await app.assertScreenshot(.authenticationFlow, step: 1) @@ -91,7 +91,7 @@ class AuthenticationFlowCoordinatorUITests: XCTestCase { app.buttons[A11yIdentifiers.serverConfirmationScreen.changeServer].tap() // Server Selection: Clear the default, enter OIDC server and continue. - app.textFields[A11yIdentifiers.changeServerScreen.server].clearAndTypeText("company.com\n") + app.textFields[A11yIdentifiers.changeServerScreen.server].clearAndTypeText("company.com\n", app: app) // Server Confirmation: Tap continue button app.buttons[A11yIdentifiers.serverConfirmationScreen.continue].tap() diff --git a/UITests/Sources/BugReportUITests.swift b/UITests/Sources/BugReportUITests.swift index 03b8ec214c..d87441c964 100644 --- a/UITests/Sources/BugReportUITests.swift +++ b/UITests/Sources/BugReportUITests.swift @@ -20,13 +20,13 @@ class BugReportUITests: XCTestCase { let app = Application.launch(.bugReport) // Type 4 characters and the send button should be disabled. - app.textViews[A11yIdentifiers.bugReportScreen.report].clearAndTypeText("Text") + app.textViews[A11yIdentifiers.bugReportScreen.report].clearAndTypeText("Text", app: app) XCTAssert(app.switches[A11yIdentifiers.bugReportScreen.sendLogs].isOn) XCTAssert(!app.switches[A11yIdentifiers.bugReportScreen.canContact].isOn) try await app.assertScreenshot(.bugReport, step: 2) // Type more than 4 characters and send the button should become enabled. - app.textViews[A11yIdentifiers.bugReportScreen.report].clearAndTypeText("Longer text") + app.textViews[A11yIdentifiers.bugReportScreen.report].clearAndTypeText("Longer text", app: app) XCTAssert(app.switches[A11yIdentifiers.bugReportScreen.sendLogs].isOn) XCTAssert(!app.switches[A11yIdentifiers.bugReportScreen.canContact].isOn) try await app.assertScreenshot(.bugReport, step: 3) diff --git a/UITests/Sources/RoomMembersListScreenUITests.swift b/UITests/Sources/RoomMembersListScreenUITests.swift index 3bcecdfcfe..dc4c296498 100644 --- a/UITests/Sources/RoomMembersListScreenUITests.swift +++ b/UITests/Sources/RoomMembersListScreenUITests.swift @@ -19,7 +19,7 @@ class RoomMembersListScreenUITests: XCTestCase { let app = Application.launch(.roomMembersListScreenPendingInvites) let searchBar = app.searchFields.firstMatch - searchBar.clearAndTypeText("alice\n") + searchBar.clearAndTypeText("alice\n", app: app) try await app.assertScreenshot(.roomMembersListScreenPendingInvites, step: 1) } @@ -28,7 +28,7 @@ class RoomMembersListScreenUITests: XCTestCase { let app = Application.launch(.roomMembersListScreenPendingInvites) let searchBar = app.searchFields.firstMatch - searchBar.clearAndTypeText("bob\n") + searchBar.clearAndTypeText("bob\n", app: app) try await app.assertScreenshot(.roomMembersListScreenPendingInvites, step: 2) } diff --git a/UITests/Sources/ServerSelectionUITests.swift b/UITests/Sources/ServerSelectionUITests.swift index f5003421c3..02ac4b9a73 100644 --- a/UITests/Sources/ServerSelectionUITests.swift +++ b/UITests/Sources/ServerSelectionUITests.swift @@ -34,7 +34,7 @@ class ServerSelectionUITests: XCTestCase { let app = Application.launch(.serverSelection) // When typing in an invalid homeserver - app.textFields[A11yIdentifiers.changeServerScreen.server].clearAndTypeText("thisisbad\n") // The tests only accept an address from LoginHomeserver.mockXYZ + app.textFields[A11yIdentifiers.changeServerScreen.server].clearAndTypeText("thisisbad\n", app: app) // The tests only accept an address from LoginHomeserver.mockXYZ // Then an error should be shown and the confirmation button disabled. try await app.assertScreenshot(.serverSelection, step: 2) diff --git a/UITests/Sources/StartChatScreenUITests.swift b/UITests/Sources/StartChatScreenUITests.swift index 66a8e37c7f..63d09893e6 100644 --- a/UITests/Sources/StartChatScreenUITests.swift +++ b/UITests/Sources/StartChatScreenUITests.swift @@ -17,7 +17,7 @@ class StartChatScreenUITests: XCTestCase { func testSearchWithNoResults() async throws { let app = Application.launch(.startChat) let searchField = app.searchFields.firstMatch - searchField.clearAndTypeText("None\n") + searchField.clearAndTypeText("None\n", app: app) XCTAssert(app.staticTexts[A11yIdentifiers.startChatScreen.searchNoResults].waitForExistence(timeout: 1.0)) try await app.assertScreenshot(.startChat, step: 1) } @@ -25,7 +25,7 @@ class StartChatScreenUITests: XCTestCase { func testSearchWithResults() async throws { let app = Application.launch(.startChatWithSearchResults) let searchField = app.searchFields.firstMatch - searchField.clearAndTypeText("Bob\n") + searchField.clearAndTypeText("Bob\n", app: app) XCTAssertFalse(app.staticTexts[A11yIdentifiers.startChatScreen.searchNoResults].waitForExistence(timeout: 1.0)) XCTAssertEqual(app.collectionViews.firstMatch.cells.count, 2) try await app.assertScreenshot(.startChat, step: 2) From cd59e497fb7bfe4ea1a74ec34792f65020da67ae Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Thu, 10 Oct 2024 11:27:53 +0100 Subject: [PATCH 043/114] Tweak EncryptionReset, IdentityConfirmation and SecureBackupRecovery screens. (#3391) --- .../en.lproj/Localizable.strings | 2 +- ElementX/Sources/Generated/Strings.swift | 2 +- .../View/EncryptionResetScreen.swift | 10 +++----- .../View/IdentityConfirmationScreen.swift | 25 ++++++++----------- .../View/SecureBackupRecoveryKeyScreen.swift | 8 ------ ...est_encryptionResetScreen-iPad-en-GB.1.png | 4 +-- ...st_encryptionResetScreen-iPad-pseudo.1.png | 4 +-- ...ncryptionResetScreen-iPhone-16-en-GB.1.png | 4 +-- ...cryptionResetScreen-iPhone-16-pseudo.1.png | 4 +-- ...dentityConfirmationScreen-iPad-en-GB.1.png | 4 +-- ...entityConfirmationScreen-iPad-pseudo.1.png | 4 +-- ...tyConfirmationScreen-iPhone-16-en-GB.1.png | 4 +-- ...yConfirmationScreen-iPhone-16-pseudo.1.png | 4 +-- ...ecoveryKeyScreen-iPad-en-GB.Incomplete.png | 4 +-- ...coveryKeyScreen-iPad-pseudo.Incomplete.png | 4 +-- ...ryKeyScreen-iPhone-16-en-GB.Incomplete.png | 4 +-- ...yKeyScreen-iPhone-16-pseudo.Incomplete.png | 4 +-- 17 files changed, 39 insertions(+), 56 deletions(-) diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 6416ef7e17..87127851c4 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -477,7 +477,7 @@ "screen_edit_profile_updating_details" = "Updating profile…"; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; +"screen_encryption_reset_bullet_2" = "You will lose any message history that’s stored only on the server"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index f09ba5be42..14ddac8081 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -1123,7 +1123,7 @@ internal enum L10n { internal static var screenEncryptionResetActionContinueReset: String { return L10n.tr("Localizable", "screen_encryption_reset_action_continue_reset") } /// Your account details, contacts, preferences, and chat list will be kept internal static var screenEncryptionResetBullet1: String { return L10n.tr("Localizable", "screen_encryption_reset_bullet_1") } - /// You will lose your existing message history unless it is stored on another device + /// You will lose any message history that’s stored only on the server internal static var screenEncryptionResetBullet2: String { return L10n.tr("Localizable", "screen_encryption_reset_bullet_2") } /// You will need to verify all your existing devices and contacts again internal static var screenEncryptionResetBullet3: String { return L10n.tr("Localizable", "screen_encryption_reset_bullet_3") } diff --git a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/View/EncryptionResetScreen.swift b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/View/EncryptionResetScreen.swift index ab03039bcc..54f40c4bfc 100644 --- a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/View/EncryptionResetScreen.swift +++ b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/View/EncryptionResetScreen.swift @@ -71,13 +71,9 @@ struct EncryptionResetScreen: View { @ViewBuilder private func checkMarkItem(title: String, position: ListPosition, positive: Bool) -> some View { RoundedLabelItem(title: title, listPosition: position) { - if positive { - CompoundIcon(\.check) - .foregroundColor(.compound.iconAccentPrimary) - } else { - CompoundIcon(\.close) - .foregroundColor(.compound.iconCriticalPrimary) - } + CompoundIcon(positive ? \.check : \.info) + .foregroundColor(positive ? .compound.iconAccentPrimary : .compound.iconSecondary) + .alignmentGuide(.top) { _ in 2 } } .backgroundStyle(.compound.bgCanvasDefault) } diff --git a/ElementX/Sources/Screens/Onboarding/IdentityConfirmationScreen/View/IdentityConfirmationScreen.swift b/ElementX/Sources/Screens/Onboarding/IdentityConfirmationScreen/View/IdentityConfirmationScreen.swift index 0a4120dae4..fc91466573 100644 --- a/ElementX/Sources/Screens/Onboarding/IdentityConfirmationScreen/View/IdentityConfirmationScreen.swift +++ b/ElementX/Sources/Screens/Onboarding/IdentityConfirmationScreen/View/IdentityConfirmationScreen.swift @@ -67,32 +67,27 @@ struct IdentityConfirmationScreen: View { context.send(viewAction: .otherDevice) } .buttonStyle(.compound(.primary)) - - if context.viewState.availableActions.contains(.recovery) { - Button(L10n.screenIdentityConfirmationUseRecoveryKey) { - context.send(viewAction: .recoveryKey) - } - .buttonStyle(.compound(.secondary)) - } - } else if context.viewState.availableActions.contains(.recovery) { + } + + if context.viewState.availableActions.contains(.recovery) { Button(L10n.screenIdentityConfirmationUseRecoveryKey) { context.send(viewAction: .recoveryKey) } .buttonStyle(.compound(.primary)) } + Button(L10n.screenIdentityConfirmationCannotConfirm) { + context.send(viewAction: .reset) + } + .buttonStyle(.compound(.secondary)) + if shouldShowSkipButton { - Button(L10n.actionSkip) { + Button("\(L10n.actionSkip) 🙉") { context.send(viewAction: .skip) } .buttonStyle(.compound(.plain)) + .padding(.vertical, 14) } - - Button(L10n.screenIdentityConfirmationCannotConfirm) { - context.send(viewAction: .reset) - } - .buttonStyle(.compound(.plain)) - .padding(.vertical, 14) } } diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift index f9d17174a6..c3c0bd1ac5 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift @@ -90,14 +90,6 @@ struct SecureBackupRecoveryKeyScreen: View { } .buttonStyle(.compound(.primary)) .disabled(context.confirmationRecoveryKey.isEmpty) - - Button { - context.send(viewAction: .resetEncryption) - } label: { - Text(L10n.screenIdentityConfirmationCreateNewRecoveryKey) - .padding(.vertical, 14) - } - .buttonStyle(.compound(.plain)) } } diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-en-GB.1.png index e72f4cff6b..6c6e33a78e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7b9f1160cb8fd978c7402ead8460e56939c5ab4ee7b8285131b00307426183e -size 159516 +oid sha256:35ff1ec5ba08bdcfe19c68655ea38c0a679ee896aee25b188b9667439e37c605 +size 157122 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-pseudo.1.png index 5998a09d05..dbe3171aa9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:625e6b9fccf62cbe1c71aa454f8cd66742f881c96bd661f8418173e7933d5af0 -size 218034 +oid sha256:01977f47e883168370867511229e4357a7a9c3fb5b1ec623fc618c6627c0b1c4 +size 213205 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-en-GB.1.png index b6243f868c..43ff30697b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1068d4904a804b49209262a271bfa42ee77e69b252af4ccf0cd400e5a91cf5f5 -size 108725 +oid sha256:04e69f0f8f95eef2e94d6b5ff06340b2961c19f82a65285330e8aa4299d4b7e3 +size 107557 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-pseudo.1.png index f2062a2523..d0f4fa5900 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50b378ce2f752940b0ea0ec64c1965a6e1bc73a9b234505abaec993aea3634af -size 168764 +oid sha256:8bdd88fa80104946db0c54b75d0a2b95d1283152f6a640b4de24428df41401ff +size 164076 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-en-GB.1.png index 9168f11f5f..e3d2a9b16b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2914636f691599f5f379bc099e20e5727d1222479524c0a6b4ad670dd70daa05 -size 112687 +oid sha256:ce8e325ce94e195a17c60920e9f1a51a6aa273a10e1a0b783783def5f7bcf47c +size 115820 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-pseudo.1.png index 08c759b75b..0a6b3f63f4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bae19c911103b9b2742a1f78fc0d63ba9e8b26cd35ea9e02263f2b51698a9b07 -size 125129 +oid sha256:99629bdfc2cd08f9cd928f67f4b072acc3eef1f515fa835c3878c79038accaef +size 128224 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-en-GB.1.png index ba383dfaf5..d4630e8912 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cfd45923268bc5e7755b17597290cf39451fa92abe4b514164d19fc21f934be5 -size 67005 +oid sha256:9e3b567e97c4f410a8a8b4e834957ea32336ca611cad6ccd84c2c38495dafdfa +size 69280 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-pseudo.1.png index f3434c808d..297cae6c2b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:75c432bd34524fe3bc496b6b3306010bfdf4b51b00bcbd990286a01da8f5d9a0 -size 85907 +oid sha256:cba19a5da2430d83fa60f06ea8c569570383dbe6493745bde8e2d758a047696e +size 87946 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Incomplete.png index f4524ff43a..5ffaca6543 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8373d1d1b85980b3701f809b47e923ef15ca7091337ed2ba795e69325d9acb9 -size 113493 +oid sha256:ba23d702ec00bf2e78b2b8a5ffa65231afd4447198249e68febbd0cb8b6b85ec +size 107062 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Incomplete.png index 8de933e4ec..1a9cf0a09d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f693c37838f16f111c8b5512658bf99ba0996bff89460cbb4a25d38cc308966 -size 130681 +oid sha256:1d824857d656ee6768664a0b196b3baacf2c8422a5480f5df8ac9a50af93115b +size 122503 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Incomplete.png index 96d7106a28..8d1a0e2f90 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:61d602bf32672cc1e432940a0d01c329095ba6d96d4c2db9f1ce7727fa91bf1a -size 67746 +oid sha256:5d16d499cb8f81e67e7e4fe001bf29a139494f62f6771237c8165953ff9b7beb +size 61510 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Incomplete.png index 52917f9746..0a23a94084 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:417a2c667d4d448c6dd04b097eee9c271de1be5915fd14fe6dbf909ecb6adc82 -size 91971 +oid sha256:df53744db3f78f32a376d79e2907a9c98cf265abb62ac86ec457589e66b4b6b8 +size 81702 From 13bc3a1dd40c65188645773149ee5f0ab794d219 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Thu, 10 Oct 2024 12:46:42 +0100 Subject: [PATCH 044/114] Update the secondary button stroke colour. (#3393) * Update Compound which changes the secondary button stroke colour. * Remove old UI test snapshots and update Acknowledgements. --- ElementX.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 2 +- .../Settings.bundle/Acknowledgements.plist | 8 + .../Packages/compound-ios.plist | 676 ++++++++++++++++++ ...test_homeScreenInviteCell-iPad-en-GB.1.png | 4 +- ...est_homeScreenInviteCell-iPad-pseudo.1.png | 4 +- ...homeScreenInviteCell-iPhone-16-en-GB.1.png | 4 +- ...omeScreenInviteCell-iPhone-16-pseudo.1.png | 4 +- ...dentityConfirmationScreen-iPad-en-GB.1.png | 4 +- ...entityConfirmationScreen-iPad-pseudo.1.png | 4 +- ...tyConfirmationScreen-iPhone-16-en-GB.1.png | 4 +- ...yConfirmationScreen-iPhone-16-pseudo.1.png | 4 +- .../test_joinRoomScreen-iPad-en-GB.Invite.png | 4 +- ...test_joinRoomScreen-iPad-pseudo.Invite.png | 4 +- ..._joinRoomScreen-iPhone-16-en-GB.Invite.png | 4 +- ...joinRoomScreen-iPhone-16-pseudo.Invite.png | 4 +- ...CodeLoginScreen-iPad-en-GB.Device-code.png | 4 +- ...ginScreen-iPad-en-GB.Verification-code.png | 4 +- ...odeLoginScreen-iPad-pseudo.Device-code.png | 4 +- ...inScreen-iPad-pseudo.Verification-code.png | 4 +- ...oginScreen-iPhone-16-en-GB.Device-code.png | 4 +- ...reen-iPhone-16-en-GB.Verification-code.png | 4 +- ...ginScreen-iPhone-16-pseudo.Device-code.png | 4 +- ...een-iPhone-16-pseudo.Verification-code.png | 4 +- ...lureScreen-iPad-en-GB.Identity-Changed.png | 4 +- ...eScreen-iPad-en-GB.Own-Unsigned-Device.png | 4 +- ...ilureScreen-iPad-en-GB.Unsigned-Device.png | 4 +- ...ureScreen-iPad-pseudo.Identity-Changed.png | 4 +- ...Screen-iPad-pseudo.Own-Unsigned-Device.png | 4 +- ...lureScreen-iPad-pseudo.Unsigned-Device.png | 4 +- ...creen-iPhone-16-en-GB.Identity-Changed.png | 4 +- ...en-iPhone-16-en-GB.Own-Unsigned-Device.png | 4 +- ...Screen-iPhone-16-en-GB.Unsigned-Device.png | 4 +- ...reen-iPhone-16-pseudo.Identity-Changed.png | 4 +- ...n-iPhone-16-pseudo.Own-Unsigned-Device.png | 4 +- ...creen-iPhone-16-pseudo.Unsigned-Device.png | 4 +- ...PollsHistoryScreen-iPad-en-GB.No-polls.png | 4 +- ...ollsHistoryScreen-iPad-pseudo.No-polls.png | 4 +- ...HistoryScreen-iPhone-16-en-GB.No-polls.png | 4 +- ...istoryScreen-iPhone-16-pseudo.No-polls.png | 4 +- ...nvites-1-iPad-10th-generation-en-GB.UI.png | 3 - .../invites-1-iPhone-16-en-GB.UI.png | 3 - .../invites-iPad-10th-generation-en-GB.UI.png | 3 - .../invites-iPhone-16-en-GB.UI.png | 3 - project.yml | 2 +- 45 files changed, 759 insertions(+), 87 deletions(-) create mode 100644 ElementX/SupportingFiles/Settings.bundle/Packages/compound-ios.plist delete mode 100644 UITests/Sources/__Snapshots__/Application/invites-1-iPad-10th-generation-en-GB.UI.png delete mode 100644 UITests/Sources/__Snapshots__/Application/invites-1-iPhone-16-en-GB.UI.png delete mode 100644 UITests/Sources/__Snapshots__/Application/invites-iPad-10th-generation-en-GB.UI.png delete mode 100644 UITests/Sources/__Snapshots__/Application/invites-iPhone-16-en-GB.UI.png diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 4c605dca35..b78d019787 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7914,7 +7914,7 @@ repositoryURL = "https://github.com/element-hq/compound-ios"; requirement = { kind = revision; - revision = 92110afc158ac6ee7c68d5e975144bafa6c58396; + revision = 4535fa11bba2efec8463a79deb5722b6b487d845; }; }; F76A08D0EA29A07A54F4EB4D /* XCRemoteSwiftPackageReference "swift-collections" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 250404a6ff..a99fe114cf 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -15,7 +15,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/compound-ios", "state" : { - "revision" : "92110afc158ac6ee7c68d5e975144bafa6c58396" + "revision" : "4535fa11bba2efec8463a79deb5722b6b487d845" } }, { diff --git a/ElementX/SupportingFiles/Settings.bundle/Acknowledgements.plist b/ElementX/SupportingFiles/Settings.bundle/Acknowledgements.plist index a68a19719c..6d4deb5f16 100644 --- a/ElementX/SupportingFiles/Settings.bundle/Acknowledgements.plist +++ b/ElementX/SupportingFiles/Settings.bundle/Acknowledgements.plist @@ -18,6 +18,14 @@ Type PSChildPaneSpecifier + + File + Packages/compound-ios + Title + compound-ios + Type + PSChildPaneSpecifier + File Packages/DeviceKit diff --git a/ElementX/SupportingFiles/Settings.bundle/Packages/compound-ios.plist b/ElementX/SupportingFiles/Settings.bundle/Packages/compound-ios.plist new file mode 100644 index 0000000000..696374fabc --- /dev/null +++ b/ElementX/SupportingFiles/Settings.bundle/Packages/compound-ios.plist @@ -0,0 +1,676 @@ + + + + + PreferenceSpecifiers + + + FooterText + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +<https://www.gnu.org/licenses/>. + + Type + PSGroupSpecifier + + + + diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenInviteCell-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenInviteCell-iPad-en-GB.1.png index 971be48c0f..bcb4a921f3 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenInviteCell-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenInviteCell-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d6130d54aa3bc5c5b6ccb2cc103631fff955c0619818fb0b2c63136bee0e0d7d -size 258045 +oid sha256:3420eee65c57587b3287a61345c17ca75fc79a75c7a85e23c13c8a03ddf640f5 +size 257422 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenInviteCell-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenInviteCell-iPad-pseudo.1.png index c6c16f7bb0..d49724e39d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenInviteCell-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenInviteCell-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e36fef3b91b2665fa93589788b342eea96649d4cd05a3d7ef558c241ae320a7d -size 273720 +oid sha256:886597e8eff82ba4a4cbaa47452ae9c3e702856a8935fca30a41c9f860be53ad +size 273084 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenInviteCell-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenInviteCell-iPhone-16-en-GB.1.png index 9b2c4200ff..4dd1bc0e8b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenInviteCell-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenInviteCell-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c9d4069973ae75f50b04a95e861c004df0805952c8c6904d41c4e73be159b4b -size 174878 +oid sha256:6941a571d3966f2d5de5066a95703bd10e942c8fa1d8ab7e1ea67e50877a6f21 +size 174158 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenInviteCell-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenInviteCell-iPhone-16-pseudo.1.png index ced45925db..c0ac14816b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenInviteCell-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenInviteCell-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:864a9495a46a2e9ad30cbb29dfbe3406ebaf388a196f016d6d36214897c1b69b -size 193428 +oid sha256:9a96f9368397992073d5f58d6e136182cfcb592b67c341a7d9fab4f1e4a71752 +size 193454 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-en-GB.1.png index e3d2a9b16b..8dae69713a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce8e325ce94e195a17c60920e9f1a51a6aa273a10e1a0b783783def5f7bcf47c -size 115820 +oid sha256:25d8ff95556fe43a082fa3b9608ee24c9aad7d7ba6301c08ec379a05454a8845 +size 115469 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-pseudo.1.png index 0a6b3f63f4..e314b7dfc4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:99629bdfc2cd08f9cd928f67f4b072acc3eef1f515fa835c3878c79038accaef -size 128224 +oid sha256:37ec779f712d787c348388eddc70a81616f7d99b8341afbd121a77a332b42a62 +size 127877 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-en-GB.1.png index d4630e8912..aa3e1de658 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9e3b567e97c4f410a8a8b4e834957ea32336ca611cad6ccd84c2c38495dafdfa -size 69280 +oid sha256:94b7e221c0c46d14eebd9c91e3915ea24819fd0c80d75ac73c2734f15a6a1881 +size 68993 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-pseudo.1.png index 297cae6c2b..d2501e83bf 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cba19a5da2430d83fa60f06ea8c569570383dbe6493745bde8e2d758a047696e -size 87946 +oid sha256:3126e413b4fa4d38ce378a479e3ff5d34383cf6dea456582eb2806f1e5ff2d4d +size 87719 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Invite.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Invite.png index e1d6fe4894..c20572b221 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Invite.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Invite.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd4af69b4bbdbfa84069a1ea1ef0529292cf1404ddca978353b02fa9aafe4a22 -size 1982852 +oid sha256:227bd893c17ef8a6042e6eab9372eb78c3cbb631c4b232f13d6e6f0179ce02c7 +size 1982377 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Invite.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Invite.png index a19bcbcd62..5bc940cd75 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Invite.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Invite.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21a35ce286f3b7b5001b84c2941f044483f2ca77b96d5c8367945658e732915d -size 1983924 +oid sha256:91e9b65c2ad0972f12351bad8d93c627a9d73a87cb22356cd9fc53c81625ed7d +size 1983445 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Invite.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Invite.png index c5acd00a66..c971358184 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Invite.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Invite.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bfcc6e059fd1bf8d93a5b1b4766030d1ade06cf0fb1ccdf887c3289c9649b358 -size 818216 +oid sha256:b5bb2adb3c27c6adad8d72174d662688ce1b40a90d748b00354717abf5d37f56 +size 817765 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Invite.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Invite.png index 3f1c987835..c70e647ccd 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Invite.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Invite.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6bebae9e971fb3e54e407e2b12ee145d60c1a1c72da97607feb75d2660194033 -size 820621 +oid sha256:9a922e59c0fec9924f16c3e224af836de79d818fab3e686c1fb34c707be1fc23 +size 820225 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Device-code.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Device-code.png index d1162fdea4..4c777b4a2b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Device-code.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Device-code.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52f1f3c88038a0be2f22b2172e64ab0f63f27e6f3e46ccc0e7a447e7ecee2a60 -size 117531 +oid sha256:e2424cd9bb0c636eec98a52603bcef3cf7d334f12efbdb74e0df0803d35ca1df +size 117014 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Verification-code.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Verification-code.png index 0541ae9c8b..6fdcb1907d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Verification-code.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Verification-code.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f291b9089775cddbe19b72b65690db0dc992431df77964f4ee75e438518e079 -size 122826 +oid sha256:86a40b993a17483418b5619860d7f48a08ddc86e352b0c7fabb573094c7b0222 +size 122309 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Device-code.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Device-code.png index 4a58956410..34dccdcfd7 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Device-code.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Device-code.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e7ef2f40a286ce8f2834841a37ebe3e282efd871786b3bfeca7980ccf8e7deb3 -size 139587 +oid sha256:2a68e7dc0baf9bb74a67366ce8fb7e40bd643ffb58e269da4d40c2dfa4c9c1c1 +size 139006 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Verification-code.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Verification-code.png index d1937a3e80..092800bb2b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Verification-code.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Verification-code.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc5811c244211eeeeb091b409a37d6f493ee8a55656ae63cb18f7be546f61bca -size 139318 +oid sha256:9f0b87391733076dedd6104648158fc7953e991ddea2b67d1b14cb908e8b53bc +size 138793 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Device-code.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Device-code.png index 3b6be2b034..f94d4567a7 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Device-code.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Device-code.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c083ed06f17b73d1a75f36e81c0e4678e0dbe8421fab97dc79662f457712f9f -size 70386 +oid sha256:7c4831d09c7edcaa594b5801981c165458305058df76e59e7096dddf2db0730a +size 69984 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Verification-code.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Verification-code.png index dbebf00358..d9f9554334 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Verification-code.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Verification-code.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:404e142dc91d14a2cff281674cfa7b2b288f92e484a4a2d81f613d44952aea97 -size 70641 +oid sha256:a4f911552b4aa5118c2ea9660a0c4ccfccd0132558ded80b58760e13a64ed0b6 +size 70163 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Device-code.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Device-code.png index 5dcbf12e2a..377d1c467d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Device-code.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Device-code.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82acfc609a7d505acd4f432e144e6a928f7e5d13a1c28ff3099237a248fa49a4 -size 92718 +oid sha256:1c8af4b3857547ebe5b9bcb9daf121b0465c26c4f6a5c4cfacea72dfd3a24585 +size 91363 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Verification-code.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Verification-code.png index 9375dd0bd1..f246f2fd83 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Verification-code.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Verification-code.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:382c71c5c3f52f3011cf1b820212f3f60275824bed1de9c1d276732db5d9259f -size 93276 +oid sha256:0043994a4a6d30673de03f27825bdddd76434e252379e8b12b4e287cd8932529 +size 92738 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Identity-Changed.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Identity-Changed.png index 2063294306..71249ab4eb 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Identity-Changed.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Identity-Changed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e4f2a9579f5e64da486ac22e56f1cea10f3450687641900790408f9f3004311 -size 121955 +oid sha256:af4e44604db966239fc9ca5918e3fb0cf5f9c126a1785c007a4ee7e122270e98 +size 121521 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Own-Unsigned-Device.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Own-Unsigned-Device.png index 7426febed3..77c4ea61a2 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Own-Unsigned-Device.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Own-Unsigned-Device.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4753c2858ee5ecec25a29b399e7ea80182f687c6a2f841a92a414e8e920b5924 -size 125882 +oid sha256:4fbdd56c2c86f9b49eef80d22ae63419be54ebb05a9b4f476b09ad39add633eb +size 125438 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Unsigned-Device.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Unsigned-Device.png index da16c72328..c7ee66b1f9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Unsigned-Device.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Unsigned-Device.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89391abf747aef94c9b315e19607fd4256956d269d2f53141477952e604e8d24 -size 123919 +oid sha256:fa265a85461f9bbe5b93230b04ed3efb053cb5a5491861c2da6a1680d971c1e1 +size 123492 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Identity-Changed.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Identity-Changed.png index 343d5fa491..8e5c835af9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Identity-Changed.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Identity-Changed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec75d1ba7375877c82ea1b71947607558762aa00ae4ca575bcc81a8434e9b76b -size 155316 +oid sha256:79635a61bf62cdc667612ab43065fa76e2f63accf129b29110fdc5fb8d1b5129 +size 154977 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Own-Unsigned-Device.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Own-Unsigned-Device.png index 824c7dd49b..ac16d80a94 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Own-Unsigned-Device.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Own-Unsigned-Device.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2d8fb744f1ae503247c5da24b629808edef82b99478834a31f4116d7d5db992 -size 160093 +oid sha256:a68411b261462636bc29807e3f6f146294ee0de54fc40f93bca95ac3abe45cbf +size 159605 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Unsigned-Device.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Unsigned-Device.png index a618927ca0..b8f2e5b2d0 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Unsigned-Device.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Unsigned-Device.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f58a9af3a9780ad49b7b5a90fa393b8f69bc1c752713477cbfb574389d101840 -size 159142 +oid sha256:e1c79aca6ec5dbcae46fc48f9785cb420b9ad14432c17b1090ae9ff4240cc24f +size 158716 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Identity-Changed.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Identity-Changed.png index 3108ccc31f..aec3b712fc 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Identity-Changed.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Identity-Changed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ca3e0c570345f63fe2d0e915b9cb1c68faa5758acf87901b21b674a9838b380 -size 85398 +oid sha256:8d6bf687431f2c06a8c9fc7490c6456f4bac88b53cf78a3557bdb50c96ee4c75 +size 85170 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Own-Unsigned-Device.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Own-Unsigned-Device.png index f508950f32..78979a8d12 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Own-Unsigned-Device.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Own-Unsigned-Device.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5145981b3a4ec6e66341f81fd7dac4c6229f361c22f2ceb9a2f3b442cecaec76 -size 90320 +oid sha256:aaae80c0ca61129cf141f4a2ec1c5371ef8e2d69002f3a7177af1f88c824ab1d +size 90001 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Unsigned-Device.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Unsigned-Device.png index 55ad8a9b96..a78eef7396 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Unsigned-Device.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Unsigned-Device.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c37c7425834dd88af07803bf1a414cfc6155957874cd2fe5a09fd6e17ff5458b -size 88086 +oid sha256:eeaebad0964270d0cdce3a25264e316235f0be1b7e61c1d839ead9fc17e34f2c +size 87765 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Identity-Changed.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Identity-Changed.png index 947b01f288..30cdecc698 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Identity-Changed.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Identity-Changed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb7f1eb81810906391eb771ffcec9f37357d2f340677e151edbe1b7977477137 -size 128891 +oid sha256:0f1b3131538ad1897d5c6e366736ddda5996178b6975ced6f3d4c60981a08e6d +size 128751 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Own-Unsigned-Device.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Own-Unsigned-Device.png index 0104d2f507..6ea231e2ad 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Own-Unsigned-Device.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Own-Unsigned-Device.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88602e805a91c4857f0f8101d1dd0856d92b8d3ba1335d2057e1e3cec727ec6b -size 137841 +oid sha256:9c45bc35fadc6cc5ca270991382a32f0808155234eece265bca19b6717d7ee24 +size 137666 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Unsigned-Device.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Unsigned-Device.png index b7a343a636..0173949776 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Unsigned-Device.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Unsigned-Device.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:63fbe1cf5d4c4b23b1f6fbe2b33d41c0d74654ccd33a473fd71527b7aa75a921 -size 134230 +oid sha256:f46bc4eeba947c304fa2d1e91a10e45c0800316c70741ee7a0742d23d782631d +size 133850 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomPollsHistoryScreen-iPad-en-GB.No-polls.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomPollsHistoryScreen-iPad-en-GB.No-polls.png index 10cd5de873..14c16d394a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomPollsHistoryScreen-iPad-en-GB.No-polls.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomPollsHistoryScreen-iPad-en-GB.No-polls.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:650ee52b5e515c784ef1017a7d70905487fe7778491d0495e247613319e36cb2 -size 97341 +oid sha256:a2a32054b700e0be0b85f8bc45a06376e4bf29279725ec86143871f74655cee0 +size 96858 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomPollsHistoryScreen-iPad-pseudo.No-polls.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomPollsHistoryScreen-iPad-pseudo.No-polls.png index dcdc6e5c87..ad9f082df8 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomPollsHistoryScreen-iPad-pseudo.No-polls.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomPollsHistoryScreen-iPad-pseudo.No-polls.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:734698e96823c3b2f52749b0efc8cd35df46dbd087a26e8d6beeb9d453b6490d -size 101511 +oid sha256:9245c3ef8d64fd667112ff771c0c240ec3c52551546ca4ca57e8e0bc547b63c7 +size 100766 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomPollsHistoryScreen-iPhone-16-en-GB.No-polls.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomPollsHistoryScreen-iPhone-16-en-GB.No-polls.png index 3714979731..26e689a182 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomPollsHistoryScreen-iPhone-16-en-GB.No-polls.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomPollsHistoryScreen-iPhone-16-en-GB.No-polls.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:02d61323b3873b6fcf72bde1d0fdb53087654dfc59ef4ebe733a249b916ba6ab -size 50362 +oid sha256:09954b58ccef1c0b856f364be3e0cef692d53c3a15609878723dc09ca29e68a0 +size 49753 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomPollsHistoryScreen-iPhone-16-pseudo.No-polls.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomPollsHistoryScreen-iPhone-16-pseudo.No-polls.png index 1868d0d954..6b36ee0025 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomPollsHistoryScreen-iPhone-16-pseudo.No-polls.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomPollsHistoryScreen-iPhone-16-pseudo.No-polls.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc44e2105daba0ad118dfe7b060e2e815ed2fdb0059f8d66f200ba115f44048e -size 58545 +oid sha256:a2ff718576ca8e932d02534b624fd9fd4d33925c972a04c48d97db1aab2b1665 +size 58483 diff --git a/UITests/Sources/__Snapshots__/Application/invites-1-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/invites-1-iPad-10th-generation-en-GB.UI.png deleted file mode 100644 index 13a399b55e..0000000000 --- a/UITests/Sources/__Snapshots__/Application/invites-1-iPad-10th-generation-en-GB.UI.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:48cf950be647ef9975353cdd72e4116eb39120c943af813aa6f3dcd29f5315dc -size 219745 diff --git a/UITests/Sources/__Snapshots__/Application/invites-1-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/invites-1-iPhone-16-en-GB.UI.png deleted file mode 100644 index 125db3378d..0000000000 --- a/UITests/Sources/__Snapshots__/Application/invites-1-iPhone-16-en-GB.UI.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f0653dd6a16b1180d1b6c931775809f4ab1425f477291595e8ab1cea6b536380 -size 395222 diff --git a/UITests/Sources/__Snapshots__/Application/invites-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/invites-iPad-10th-generation-en-GB.UI.png deleted file mode 100644 index a2b3a9695f..0000000000 --- a/UITests/Sources/__Snapshots__/Application/invites-iPad-10th-generation-en-GB.UI.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b63e96cf767258b3b460272dbeac43a70e1d8ef1e94795d1d6d91a1d832b0aa4 -size 118927 diff --git a/UITests/Sources/__Snapshots__/Application/invites-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/invites-iPhone-16-en-GB.UI.png deleted file mode 100644 index 36c47d6308..0000000000 --- a/UITests/Sources/__Snapshots__/Application/invites-iPhone-16-en-GB.UI.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:291f91e48e628b9ec1a047dfa13337a2b371badd7c26d56b74748b9d7ea1192f -size 142351 diff --git a/project.yml b/project.yml index b233b61c77..bef45944f8 100644 --- a/project.yml +++ b/project.yml @@ -64,7 +64,7 @@ packages: # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios - revision: 92110afc158ac6ee7c68d5e975144bafa6c58396 + revision: 4535fa11bba2efec8463a79deb5722b6b487d845 # path: ../compound-ios AnalyticsEvents: url: https://github.com/matrix-org/matrix-analytics-events From 4d697a3a492014125bd6933c44f4dfe4d130901a Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Thu, 10 Oct 2024 15:04:17 +0300 Subject: [PATCH 045/114] Introduce a feature flag for the new identity pinning violation notifications feature. --- ElementX/Sources/Application/AppSettings.swift | 4 ++++ ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift | 4 +++- .../DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift | 1 + .../DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift | 4 ++++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index ec0dc26b69..16c74d9fbe 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -46,6 +46,7 @@ final class AppSettings { case fuzzyRoomListSearchEnabled case pinningEnabled case enableOnlySignedDeviceIsolationMode + case identityPinningViolationNotificationsEnabled } private static var suiteName: String = InfoPlistReader.main.appGroupIdentifier @@ -278,6 +279,9 @@ final class AppSettings { enum SlidingSyncDiscovery: Codable { case proxy, native, forceNative } @UserPreference(key: UserDefaultsKeys.slidingSyncDiscovery, defaultValue: .native, storageType: .userDefaults(store)) var slidingSyncDiscovery: SlidingSyncDiscovery + + @UserPreference(key: UserDefaultsKeys.identityPinningViolationNotificationsEnabled, defaultValue: isDevelopmentBuild, storageType: .userDefaults(store)) + var identityPinningViolationNotificationsEnabled #endif diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index 36f3bb7239..956ae8210a 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -142,7 +142,9 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } .store(in: &cancellables) - let identityStatusChangesPublisher = roomProxy.identityStatusChangesPublisher.receive(on: DispatchQueue.main) + let identityStatusChangesPublisher = roomProxy.identityStatusChangesPublisher + .receive(on: DispatchQueue.main) + .filter { [weak self] _ in self?.appSettings.identityPinningViolationNotificationsEnabled ?? false } Task { [weak self] in for await changes in identityStatusChangesPublisher.values { diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift index d2f9f7a4b4..4bff8d3690 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift @@ -48,6 +48,7 @@ protocol DeveloperOptionsProtocol: AnyObject { var hideTimelineMedia: Bool { get set } var enableOnlySignedDeviceIsolationMode: Bool { get set } var elementCallBaseURLOverride: URL? { get set } + var identityPinningViolationNotificationsEnabled: Bool { get set } } extension AppSettings: DeveloperOptionsProtocol { } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift index 878efcb09e..df1afd5400 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -49,6 +49,10 @@ struct DeveloperOptionsScreen: View { Toggle(isOn: $context.hideTimelineMedia) { Text("Hide image & video previews") } + + Toggle(isOn: $context.identityPinningViolationNotificationsEnabled) { + Text("Identity pinning violation notifications") + } } Section { From 8e03cc862d32641d018d67bd2e848a39f7cd65fb Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Thu, 10 Oct 2024 14:39:10 +0100 Subject: [PATCH 046/114] Show the Login with QR Code button. (#3392) --- .../AuthenticationStartScreenViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenViewModel.swift b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenViewModel.swift index 10f9b16103..a390d7aab7 100644 --- a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenViewModel.swift +++ b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenViewModel.swift @@ -19,7 +19,7 @@ class AuthenticationStartScreenViewModel: AuthenticationStartScreenViewModelType init(webRegistrationEnabled: Bool) { super.init(initialViewState: AuthenticationStartScreenViewState(isWebRegistrationEnabled: webRegistrationEnabled, - isQRCodeLoginEnabled: !ProcessInfo.processInfo.isiOSAppOnMac && AppSettings.isDevelopmentBuild)) + isQRCodeLoginEnabled: !ProcessInfo.processInfo.isiOSAppOnMac)) } override func process(viewAction: AuthenticationStartScreenViewAction) { From 2c1ae9a93d6922aab755434b17c5d5a8d62af996 Mon Sep 17 00:00:00 2001 From: Element CI Date: Thu, 10 Oct 2024 07:01:14 -0700 Subject: [PATCH 047/114] Prepare next release --- CHANGES.md | 21 +++++++++++++++++++++ ElementX.xcodeproj/project.pbxproj | 4 ++-- project.yml | 2 +- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 206778f122..857e47b2a8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,24 @@ +## Changes in 1.8.6 (2024-10-10) + +### What's Changed + +✨ Features +* crypto: Configure decryption trustRequirement based on config flag by @BillCarsonFr in https://github.com/element-hq/element-x-ios/pull/3358 +* Introduce a feature flag for the new identity pinning violation notifications feature by @stefanceriu in https://github.com/element-hq/element-x-ios/pull/3394 +* Show the Login with QR Code button. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3392 + +🙌 Improvements +* Add a subtitle to the QR Code login instructions. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3386 +* Tweak the UI in the EncryptionReset, IdentityConfirmation and SecureBackupRecovery screens. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3391 +* Update the secondary button stroke colour. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3393 + +Others +* Fix an authentication UI test snapshot. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3387 +* Ask the iPad to reveal the keyboard in UI Tests when it's hidden. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3389 + + +**Full Changelog**: https://github.com/element-hq/element-x-ios/compare/1.8.5...1.8.6 + ## Changes in 1.8.5 (2024-10-08) ### What's Changed diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index b78d019787..c9964b06e6 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7433,7 +7433,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.8.6; + MARKETING_VERSION = 1.8.7; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCTION_APP_NAME = Element; @@ -7510,7 +7510,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.8.6; + MARKETING_VERSION = 1.8.7; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; diff --git a/project.yml b/project.yml index bef45944f8..9400fc4941 100644 --- a/project.yml +++ b/project.yml @@ -41,7 +41,7 @@ settings: APP_GROUP_IDENTIFIER: group.$(BASE_APP_GROUP_IDENTIFIER) APP_NAME: ElementX KEYCHAIN_ACCESS_GROUP_IDENTIFIER: "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)" - MARKETING_VERSION: 1.8.6 + MARKETING_VERSION: 1.8.7 CURRENT_PROJECT_VERSION: 1 SUPPORTS_MACCATALYST: false From 71d6fcda26f42866bd0bc0daf9392f34885559ca Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Thu, 10 Oct 2024 16:46:09 +0100 Subject: [PATCH 048/114] Fix identity pinning link. (#3395) --- ElementX/Sources/Application/AppSettings.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index 16c74d9fbe..66a4ef7831 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -126,7 +126,7 @@ final class AppSettings { /// A URL where users can go read more about the chat backup. let chatBackupDetailsURL: URL = "https://element.io/help#encryption5" /// A URL where users can go read more about identity pinning violations - let identityPinningViolationDetailsURL: URL = "https://element.io/help#18" + let identityPinningViolationDetailsURL: URL = "https://element.io/help#encryption18" /// Any domains that Element web may be hosted on - used for handling links. let elementWebHosts = ["app.element.io", "staging.element.io", "develop.element.io"] From a7ba32a1a7165d0ebce946495fc2222050bbb504 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Thu, 10 Oct 2024 17:15:29 +0100 Subject: [PATCH 049/114] =?UTF-8?q?Version=201.9.0=20=F0=9F=98=8E=20(#3396?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ElementX.xcodeproj/project.pbxproj | 4 ++-- project.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index c9964b06e6..a5d5af1f48 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7433,7 +7433,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.8.7; + MARKETING_VERSION = 1.9.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCTION_APP_NAME = Element; @@ -7510,7 +7510,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.8.7; + MARKETING_VERSION = 1.9.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; diff --git a/project.yml b/project.yml index 9400fc4941..146b2f4695 100644 --- a/project.yml +++ b/project.yml @@ -41,7 +41,7 @@ settings: APP_GROUP_IDENTIFIER: group.$(BASE_APP_GROUP_IDENTIFIER) APP_NAME: ElementX KEYCHAIN_ACCESS_GROUP_IDENTIFIER: "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)" - MARKETING_VERSION: 1.8.7 + MARKETING_VERSION: 1.9.0 CURRENT_PROJECT_VERSION: 1 SUPPORTS_MACCATALYST: false From 4165bef5e82375b205ace01931c12f53cbcea592 Mon Sep 17 00:00:00 2001 From: Element CI Date: Thu, 10 Oct 2024 09:41:27 -0700 Subject: [PATCH 050/114] Prepare next release --- CHANGES.md | 13 +++++++++++++ ElementX.xcodeproj/project.pbxproj | 4 ++-- project.yml | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 857e47b2a8..879a55503a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,16 @@ +## Changes in 1.9.0 (2024-10-10) + +### What's Changed + +🐛 Bugfixes +* Fix identity pinning link. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3395 + +🧱 Build +* Update the version to 1.9.0. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3396 + + +**Full Changelog**: https://github.com/element-hq/element-x-ios/compare/1.8.6...1.9.0 + ## Changes in 1.8.6 (2024-10-10) ### What's Changed diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index a5d5af1f48..866bf06098 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7433,7 +7433,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.9.0; + MARKETING_VERSION = 1.9.1; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCTION_APP_NAME = Element; @@ -7510,7 +7510,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.9.0; + MARKETING_VERSION = 1.9.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; diff --git a/project.yml b/project.yml index 146b2f4695..e746561dc3 100644 --- a/project.yml +++ b/project.yml @@ -41,7 +41,7 @@ settings: APP_GROUP_IDENTIFIER: group.$(BASE_APP_GROUP_IDENTIFIER) APP_NAME: ElementX KEYCHAIN_ACCESS_GROUP_IDENTIFIER: "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)" - MARKETING_VERSION: 1.9.0 + MARKETING_VERSION: 1.9.1 CURRENT_PROJECT_VERSION: 1 SUPPORTS_MACCATALYST: false From 25037a67eec9ac0594b870546a79a6cb152e9f1f Mon Sep 17 00:00:00 2001 From: ElementRobot Date: Mon, 14 Oct 2024 10:29:14 +0100 Subject: [PATCH 051/114] Translations update (#3406) Co-authored-by: Velin92 <34335419+Velin92@users.noreply.github.com> --- .../be.lproj/Localizable.strings | 6 ++++ .../bg.lproj/Localizable.strings | 8 ++++- .../cs.lproj/Localizable.strings | 12 ++++++-- .../de.lproj/Localizable.strings | 6 ++++ .../el.lproj/Localizable.strings | 6 ++++ .../en.lproj/Localizable.strings | 5 ++++ .../es.lproj/Localizable.strings | 8 ++++- .../et.lproj/Localizable.strings | 6 ++++ .../fa.lproj/Localizable.strings | 6 ++++ .../fr.lproj/Localizable.strings | 12 ++++++-- .../hu.lproj/Localizable.strings | 6 ++++ .../id.lproj/Localizable.strings | 8 ++++- .../it.lproj/Localizable.strings | 6 ++++ .../ka.lproj/Localizable.strings | 8 ++++- .../nl.lproj/Localizable.strings | 30 +++++++++++-------- .../pl.lproj/Localizable.strings | 6 ++++ .../pt-BR.lproj/Localizable.strings | 8 ++++- .../pt.lproj/Localizable.strings | 8 ++++- .../ro.lproj/Localizable.strings | 8 ++++- .../ru.lproj/Localizable.strings | 6 ++++ .../sk.lproj/Localizable.strings | 6 ++++ .../sv.lproj/Localizable.strings | 6 ++++ .../uk.lproj/Localizable.strings | 6 ++++ .../uz.lproj/Localizable.strings | 8 ++++- .../zh-Hans.lproj/Localizable.strings | 6 ++++ .../zh-Hant-TW.lproj/Localizable.strings | 8 ++++- ElementX/Sources/Generated/Strings.swift | 10 +++++++ 27 files changed, 192 insertions(+), 27 deletions(-) diff --git a/ElementX/Resources/Localizations/be.lproj/Localizable.strings b/ElementX/Resources/Localizations/be.lproj/Localizable.strings index 1017183541..b12ba3154e 100644 --- a/ElementX/Resources/Localizations/be.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/be.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "Карыстальніцкі URL сервера Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Усталюйце карыстальніцкі асноўны URL для Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Адрас пазначаны няправільна, пераканайцеся, што вы ўказалі пратакол (http/https) і правільны адрас."; +"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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Націсніце на паведамленне і абярыце «%1$@ », каб уключыць сюды."; "screen_pinned_timeline_empty_state_headline" = "Замацуеце важныя паведамленні, каб іх можна было лёгка знайсці"; "screen_reset_encryption_password_error" = "Адбылася невядомая памылка. Калі ласка, праверце правільнасць пароля вашага ўліковага запісу і паўтарыце спробу."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Выберыце %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Звязаць новую прыладу”"; "screen_qr_code_login_initial_state_item_4" = "Адсканіруйце QR-код з дапамогай гэтай прылады"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Адкрыйце %1$@ на іншай прыладзе, каб атрымаць QR-код"; "screen_qr_code_login_invalid_scan_state_description" = "Выкарыстоўвайце QR-код, паказаны на іншай прыладзе."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Няправільны QR-код"; diff --git a/ElementX/Resources/Localizations/bg.lproj/Localizable.strings b/ElementX/Resources/Localizations/bg.lproj/Localizable.strings index b459c6d4da..7f1f532607 100644 --- a/ElementX/Resources/Localizations/bg.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/bg.lproj/Localizable.strings @@ -334,6 +334,11 @@ "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_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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -477,7 +482,7 @@ "screen_edit_profile_updating_details" = "Обновяване на профила…"; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; +"screen_encryption_reset_bullet_2" = "You will lose any message history that’s stored only on the server"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Select %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Link new device”"; "screen_qr_code_login_initial_state_item_4" = "Scan the QR code with this device"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Open %1$@ on another device to get the QR code"; "screen_qr_code_login_invalid_scan_state_description" = "Use the QR code shown on the other device."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Wrong QR code"; diff --git a/ElementX/Resources/Localizations/cs.lproj/Localizable.strings b/ElementX/Resources/Localizations/cs.lproj/Localizable.strings index 34dbc0ec9f..47ff860bb8 100644 --- a/ElementX/Resources/Localizations/cs.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/cs.lproj/Localizable.strings @@ -94,7 +94,7 @@ "action_send_message" = "Odeslat zprávu"; "action_share" = "Sdílet"; "action_share_link" = "Sdílet odkaz"; -"action_show" = "Show"; +"action_show" = "Zobrazit"; "action_sign_in_again" = "Přihlásit se znovu"; "action_signout" = "Odhlásit se"; "action_signout_anyway" = "Přesto se odhlásit"; @@ -231,7 +231,7 @@ "common_voice_message" = "Hlasová zpráva"; "common_waiting" = "Čekání..."; "common_waiting_for_decryption_key" = "Čekání na dešifrovací klíč"; -"common.copied_to_clipboard" = "Copied to clipboard"; +"common.copied_to_clipboard" = "Zkopírováno do schránky"; "common.do_not_show_this_again" = "Znovu nezobrazovat"; "common.open_source_licenses" = "Licence s otevřeným zdrojovým kódem"; "common.pinned" = "Připnuto"; @@ -240,7 +240,7 @@ "confirm_recovery_key_banner_message" = "Vaše záloha chatu není aktuálně synchronizována. Abyste si zachovali přístup k záloze chatu, musíte potvrdit klíč pro obnovení."; "confirm_recovery_key_banner_title" = "Potvrďte klíč pro obnovení"; "crash_detection_dialog_content" = "%1$@ havaroval při posledním použití. Chcete se s námi podělit o zprávu o selhání?"; -"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation" = "Zdá se, že se identita %1$@ změnila. %2$@"; "dialog_permission_camera" = "Aby mohla aplikace používat fotoaparát, udělte prosím oprávnění v nastavení systému."; "dialog_permission_generic" = "Udělte prosím oprávnění v nastavení systému."; "dialog_permission_location_description_ios" = "Udělte přístup v Nastavení -> Poloha."; @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "Vlastní URL pro Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Nastavte vlastní URL pro Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Neplatné URL, ujistěte se, že jste uvedli protokol (http/https) a správnou adresu."; +"screen_create_room_access_section_anyone_option_description" = "Do této místnosti může vstoupit kdokoli"; +"screen_create_room_access_section_anyone_option_title" = "Kdokoliv"; +"screen_create_room_access_section_header" = "Přístup do místnosti"; +"screen_create_room_access_section_knocking_option_description" = "Kdokoli může požádat o vstup do místnosti, ale správce nebo moderátor bude muset žádost přijmout"; +"screen_create_room_access_section_knocking_option_title" = "Požádat o připojení"; "screen_pinned_timeline_empty_state_description" = "Přidržte zprávu a vyberte „%1$@“, kterou chcete zahrnout sem."; "screen_pinned_timeline_empty_state_headline" = "Připněte důležité zprávy, aby je bylo možné snadno najít"; "screen_reset_encryption_password_error" = "Došlo k neznámé chybě. Zkontrolujte, zda je heslo k účtu správné a zkuste to znovu."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Vybrat %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "\"Připojit nové zařízení\""; "screen_qr_code_login_initial_state_item_4" = "Naskenujte QR kód pomocí tohoto zařízení"; +"screen_qr_code_login_initial_state_subtitle" = "Dostupné pouze v případě, že to váš poskytovatel účtu podporuje."; "screen_qr_code_login_initial_state_title" = "Otevřete %1$@ na jiném zařízení pro získání QR kódu"; "screen_qr_code_login_invalid_scan_state_description" = "Použijte QR kód zobrazený na druhém zařízení."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Špatný QR kód"; diff --git a/ElementX/Resources/Localizations/de.lproj/Localizable.strings b/ElementX/Resources/Localizations/de.lproj/Localizable.strings index ad02436387..8c197aa41b 100644 --- a/ElementX/Resources/Localizations/de.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/de.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "Benutzerdefinierte Element-Aufruf-Basis-URL"; "screen_advanced_settings_element_call_base_url_description" = "Lege eine eigene Basis-URL für Element Call fest."; "screen_advanced_settings_element_call_base_url_validation_error" = "Ungültige URL, bitte stelle sicher, dass du das Protokoll (http/https) und die richtige Adresse angibst."; +"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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Drücke auf eine Nachricht und wähle “%1$@”, um sie hier einzufügen."; "screen_pinned_timeline_empty_state_headline" = "Fixiere wichtige Nachrichten, so dass sie leicht gefunden werden können"; "screen_reset_encryption_password_error" = "Es ist ein unbekannter Fehler aufgetreten. Bitte überprüfe das Passwort deines Kontos und versuche es erneut."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Wähle %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "\"Neues Gerät verknüpfen\""; "screen_qr_code_login_initial_state_item_4" = "Scanne den QR-Code mit diesem Gerät"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Öffne %1$@ auf einem anderen Gerät, um den QR-Code zu erhalten"; "screen_qr_code_login_invalid_scan_state_description" = "Verwende den QR-Code, der auf dem anderen Gerät angezeigt wird."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Falscher QR-Code"; diff --git a/ElementX/Resources/Localizations/el.lproj/Localizable.strings b/ElementX/Resources/Localizations/el.lproj/Localizable.strings index 8c7fdf9943..e3eb06a1e1 100644 --- a/ElementX/Resources/Localizations/el.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/el.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "Προσαρμοσμένο URL βάσης κλήσεων Element"; "screen_advanced_settings_element_call_base_url_description" = "Όρισε μια προσαρμοσμένη διεύθυνση βάσης URL για κλήση Element."; "screen_advanced_settings_element_call_base_url_validation_error" = "Μη έγκυρη διεύθυνση URL, βεβαιώσου ότι έχεις συμπεριλάβει το πρωτόκολλο (http/https) και τη σωστή διεύθυνση."; +"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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Πάτα σε ένα μήνυμα και επέλεξε «%1$@» για να συμπεριληφθεί εδώ."; "screen_pinned_timeline_empty_state_headline" = "Καρφίτσωσε σημαντικά μηνύματα, ώστε να μπορούν να εντοπιστούν εύκολα"; "screen_reset_encryption_password_error" = "Συνέβη ένα άγνωστο σφάλμα. Έλεγξε ότι ο κωδικός πρόσβασης του λογαριασμού σου είναι σωστός και δοκίμασε ξανά."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Επιλογή %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "«Σύνδεση νέας συσκευής»"; "screen_qr_code_login_initial_state_item_4" = "Σάρωσε τον κωδικό QR με αυτήν τη συσκευή"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Άνοιγμα %1$@ σε άλλη συσκευή για να λήψη κωδικού QR"; "screen_qr_code_login_invalid_scan_state_description" = "Χρησιμοποίησε τον κωδικό QR που εμφανίζεται στην άλλη συσκευή."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Λάθος κωδικός QR"; diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 87127851c4..b4aadb523e 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -334,6 +334,11 @@ "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_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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; diff --git a/ElementX/Resources/Localizations/es.lproj/Localizable.strings b/ElementX/Resources/Localizations/es.lproj/Localizable.strings index 6773767eff..342eefefa1 100644 --- a/ElementX/Resources/Localizations/es.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/es.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "URL base personalizada de Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Define una URL base personalizada para Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL no válida, asegúrate de incluir el protocolo (http/https) y la dirección correcta."; +"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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -477,7 +482,7 @@ "screen_edit_profile_updating_details" = "Actualizando perfil..."; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; +"screen_encryption_reset_bullet_2" = "You will lose any message history that’s stored only on the server"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Select %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Link new device”"; "screen_qr_code_login_initial_state_item_4" = "Scan the QR code with this device"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Open %1$@ on another device to get the QR code"; "screen_qr_code_login_invalid_scan_state_description" = "Use the QR code shown on the other device."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Wrong QR code"; diff --git a/ElementX/Resources/Localizations/et.lproj/Localizable.strings b/ElementX/Resources/Localizations/et.lproj/Localizable.strings index ea8ee03b8b..fdd05262ed 100644 --- a/ElementX/Resources/Localizations/et.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/et.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "Element Calli kohandatud teenuseaadress"; "screen_advanced_settings_element_call_base_url_description" = "Seadista kohandatud teenuseaadress Element Calli jaoks."; "screen_advanced_settings_element_call_base_url_validation_error" = "Vigane url. Palun vaata, et url algaks protokolliga (http/https) ning aadress ise oleks ka õige."; +"screen_create_room_access_section_anyone_option_description" = "Kõik võivad selle jututoaga liituda"; +"screen_create_room_access_section_anyone_option_title" = "Kõik"; +"screen_create_room_access_section_header" = "Ligipääs jututoale"; +"screen_create_room_access_section_knocking_option_description" = "Kõik võivad paluda selle jututoaga liitumist, kuid peakasutaja või moderaator peavad selle kinnitama"; +"screen_create_room_access_section_knocking_option_title" = "Küsi võimalust liitumiseks"; "screen_pinned_timeline_empty_state_description" = "Siia lisamiseks vajuta sõnumil ja vali „%1$@“."; "screen_pinned_timeline_empty_state_headline" = "Et olulisi sõnumeid oleks lihtsam leida, tõsta nad esile"; "screen_reset_encryption_password_error" = "Tekkis teadmata viga. Palun kontrolli, kas sinu kasutajakonto salasõna on õige ja proovi uuesti."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Vali %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "„Seo uus seade“"; "screen_qr_code_login_initial_state_item_4" = "Skaneeri QR-koodi selle seadmega"; +"screen_qr_code_login_initial_state_subtitle" = "See funktsionaalsus on sadaval vaid siis, kui sinu teenusepakkuja seda toetab."; "screen_qr_code_login_initial_state_title" = "QR-koodi saamiseks ava %1$@ oma teises seadmes"; "screen_qr_code_login_invalid_scan_state_description" = "Kasuta teises seadmes näidatavat QR-koodi"; "screen_qr_code_login_invalid_scan_state_subtitle" = "Vale QR-kood"; diff --git a/ElementX/Resources/Localizations/fa.lproj/Localizable.strings b/ElementX/Resources/Localizations/fa.lproj/Localizable.strings index e8d4f5b4c9..60d656c6db 100644 --- a/ElementX/Resources/Localizations/fa.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/fa.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "نشانی پایهٔ تماس المنتی سفارشی"; "screen_advanced_settings_element_call_base_url_description" = "تنظمی نشانی پایه‌‌ای سفارشی برای تماس المنتی."; "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_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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "گزینش %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "«پیوند افزارهٔ جدید»"; "screen_qr_code_login_initial_state_item_4" = "پویش کد پاس با این افزاره"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "گشودن %1$@ روی افزاره‌ای دیگر برای گرفتن کد پاس"; "screen_qr_code_login_invalid_scan_state_description" = "استفاده از کد پاس نشان داده روی افزارهٔ دیگر."; "screen_qr_code_login_invalid_scan_state_subtitle" = "کد پاس اشتباه"; diff --git a/ElementX/Resources/Localizations/fr.lproj/Localizable.strings b/ElementX/Resources/Localizations/fr.lproj/Localizable.strings index 3a6034d05f..641e4778ad 100644 --- a/ElementX/Resources/Localizations/fr.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/fr.lproj/Localizable.strings @@ -94,7 +94,7 @@ "action_send_message" = "Envoyer un message"; "action_share" = "Partager"; "action_share_link" = "Partager le lien"; -"action_show" = "Show"; +"action_show" = "Afficher"; "action_sign_in_again" = "Se connecter à nouveau"; "action_signout" = "Se déconnecter"; "action_signout_anyway" = "Se déconnecter quand même"; @@ -231,7 +231,7 @@ "common_voice_message" = "Message vocal"; "common_waiting" = "En attente..."; "common_waiting_for_decryption_key" = "En attente de la clé de déchiffrement"; -"common.copied_to_clipboard" = "Copied to clipboard"; +"common.copied_to_clipboard" = "Copié dans le presse-papiers"; "common.do_not_show_this_again" = "Ne plus afficher"; "common.open_source_licenses" = "Licences open source"; "common.pinned" = "Épinglé"; @@ -240,7 +240,7 @@ "confirm_recovery_key_banner_message" = "La sauvegarde des conversations est désynchronisée. Vous devez confirmer la clé de récupération pour accéder à votre historique."; "confirm_recovery_key_banner_title" = "Confirmer votre clé de récupération"; "crash_detection_dialog_content" = "%1$@ s’est arrêté la dernière fois qu’il a été utilisé. Souhaitez-vous partager un rapport d’incident avec nous ?"; -"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation" = "L'identité de %1$@ semble avoir changé. %2$@"; "dialog_permission_camera" = "Pour permettre à l’application d’utiliser l’appareil photo, veuillez accorder l’autorisation dans les paramètres du système."; "dialog_permission_generic" = "Veuillez accorder l’autorisation dans les paramètres du système."; "dialog_permission_location_description_ios" = "Autorisez l’accès dans Paramètres / Localisation."; @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "URL de base pour Element Call personnalisée"; "screen_advanced_settings_element_call_base_url_description" = "Configurer une URL de base pour Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL invalide, assurez-vous d’inclure le protocol (http/https) et l’adresse correcte."; +"screen_create_room_access_section_anyone_option_description" = "Tout le monde peut rejoindre ce salon"; +"screen_create_room_access_section_anyone_option_title" = "Tout le monde"; +"screen_create_room_access_section_header" = "Accès au salon"; +"screen_create_room_access_section_knocking_option_description" = "Tout le monde peut demander à rejoindre le salon, mais un administrateur ou un modérateur devra accepter la demande"; +"screen_create_room_access_section_knocking_option_title" = "Demander à rejoindre"; "screen_pinned_timeline_empty_state_description" = "Cliquez (clic long) sur un message et choisissez « %1$@ » pour qu‘il apparaisse ici."; "screen_pinned_timeline_empty_state_headline" = "Épinglez les messages importants pour leur donner plus de visibilité"; "screen_reset_encryption_password_error" = "Une erreur s'est produite. Vérifiez que le mot de passe de votre compte est correct et réessayez."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Choisissez %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Associer une nouvelle session”"; "screen_qr_code_login_initial_state_item_4" = "Scanner le code QR avec cet appareil"; +"screen_qr_code_login_initial_state_subtitle" = "Disponible uniquement si votre fournisseur de compte le supporte."; "screen_qr_code_login_initial_state_title" = "Ouvrez %1$@ sur un autre appareil pour obtenir le QR code"; "screen_qr_code_login_invalid_scan_state_description" = "Scannez le QR code affiché sur l'autre appareil."; "screen_qr_code_login_invalid_scan_state_subtitle" = "QR code erroné"; diff --git a/ElementX/Resources/Localizations/hu.lproj/Localizable.strings b/ElementX/Resources/Localizations/hu.lproj/Localizable.strings index 1e7a98fafa..d4fc18347b 100644 --- a/ElementX/Resources/Localizations/hu.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/hu.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "Egyéni Element Call alapwebcím"; "screen_advanced_settings_element_call_base_url_description" = "Egyéni alapwebcím beállítása az Element Callhoz."; "screen_advanced_settings_element_call_base_url_validation_error" = "Érvénytelen webcím, győződjön meg arról, hogy szerepel-e benne a protokoll (http/https), és hogy helyes-e a cím."; +"screen_create_room_access_section_anyone_option_description" = "Bárki csatlakozhat ehhez a szobához"; +"screen_create_room_access_section_anyone_option_title" = "Bárki"; +"screen_create_room_access_section_header" = "Szobahozzáférés"; +"screen_create_room_access_section_knocking_option_description" = "Bárki kérheti, hogy csatlakozzon a szobához, de egy adminisztrátornak vagy moderátornak el kell fogadnia a kérést"; +"screen_create_room_access_section_knocking_option_title" = "Csatlakozás kérése"; "screen_pinned_timeline_empty_state_description" = "Nyomjon hosszan az üzenetre, és válassza a „%1$@” lehetőséget, hogy itt szerepeljen."; "screen_pinned_timeline_empty_state_headline" = "Tűzze ki a fontos üzeneteket, hogy könnyen felfedezhetők legyenek"; "screen_reset_encryption_password_error" = "Ismeretlen hiba történt. Ellenőrizze, hogy a fiókja jelszava helyes-e, és próbálja meg újra."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Válassza ezt: %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "„Új eszköz összekapcsolása”"; "screen_qr_code_login_initial_state_item_4" = "Olvassa be a QR-kódot ezzel az eszközzel"; +"screen_qr_code_login_initial_state_subtitle" = "Csak akkor érhető el, ha a fiókszolgáltató támogatja."; "screen_qr_code_login_initial_state_title" = "Nyissa meg az %1$@et egy másik eszközön a QR-kód lekéréséhez."; "screen_qr_code_login_invalid_scan_state_description" = "Használja a másik eszközön látható QR-kódot."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Hibás QR-kód"; diff --git a/ElementX/Resources/Localizations/id.lproj/Localizable.strings b/ElementX/Resources/Localizations/id.lproj/Localizable.strings index c968b46e9e..b29e7e82c9 100644 --- a/ElementX/Resources/Localizations/id.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/id.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "URL dasar Element Call khusus"; "screen_advanced_settings_element_call_base_url_description" = "Tetapkan URL dasar khusus untuk Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL tidak valid, pastikan Anda menyertakan protokol (http/https) dan alamat yang benar."; +"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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -477,7 +482,7 @@ "screen_edit_profile_updating_details" = "Memperbarui profil…"; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; +"screen_encryption_reset_bullet_2" = "You will lose any message history that’s stored only on the server"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Pilih %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Tautkan perangkat baru”"; "screen_qr_code_login_initial_state_item_4" = "Pindai kode QR dengan perangkat ini"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Buka %1$@ di perangkat lain untuk mendapatkan kode QR"; "screen_qr_code_login_invalid_scan_state_description" = "Gunakan kode QR yang ditampilkan di perangkat lain."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Kode QR salah"; diff --git a/ElementX/Resources/Localizations/it.lproj/Localizable.strings b/ElementX/Resources/Localizations/it.lproj/Localizable.strings index ad7756355f..6822eafb86 100644 --- a/ElementX/Resources/Localizations/it.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/it.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "URL base di Element Call personalizzato"; "screen_advanced_settings_element_call_base_url_description" = "Imposta un URL di base personalizzato per Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL non valido, assicurati di includere il protocollo (http/https) e l'indirizzo corretto."; +"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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Premi su un messaggio e scegli “%1$@” per includerlo qui."; "screen_pinned_timeline_empty_state_headline" = "Fissa i messaggi importanti così che possano essere trovati facilmente"; "screen_reset_encryption_password_error" = "Si è verificato un errore sconosciuto. Controlla che la password del tuo account sia corretta e riprova."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Seleziona %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "\"Collega un nuovo dispositivo\""; "screen_qr_code_login_initial_state_item_4" = "Scansiona il codice QR con questo dispositivo"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Apri %1$@ su un altro dispositivo per ottenere il codice QR"; "screen_qr_code_login_invalid_scan_state_description" = "Usa il codice QR mostrato sull'altro dispositivo."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Codice QR sbagliato"; diff --git a/ElementX/Resources/Localizations/ka.lproj/Localizable.strings b/ElementX/Resources/Localizations/ka.lproj/Localizable.strings index 342c48f297..c5915b3f2c 100644 --- a/ElementX/Resources/Localizations/ka.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ka.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "მორგებული Element-ის ზარის საბაზისო URL"; "screen_advanced_settings_element_call_base_url_description" = "დააყენეთ საბაზისო URL Element-ის ზარებისათვის."; "screen_advanced_settings_element_call_base_url_validation_error" = "არასწორი URL, გთხოვთ, დარწმუნდეთ, რომ შეიტანეთ პროტოკოლი (http/https) და სწორი მისამართი."; +"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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -477,7 +482,7 @@ "screen_edit_profile_updating_details" = "პროფილის განახლება..."; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; +"screen_encryption_reset_bullet_2" = "You will lose any message history that’s stored only on the server"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Select %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Link new device”"; "screen_qr_code_login_initial_state_item_4" = "Scan the QR code with this device"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Open %1$@ on another device to get the QR code"; "screen_qr_code_login_invalid_scan_state_description" = "Use the QR code shown on the other device."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Wrong QR code"; diff --git a/ElementX/Resources/Localizations/nl.lproj/Localizable.strings b/ElementX/Resources/Localizations/nl.lproj/Localizable.strings index 1b308038b7..50a55f8d49 100644 --- a/ElementX/Resources/Localizations/nl.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/nl.lproj/Localizable.strings @@ -78,7 +78,7 @@ "action_quick_reply" = "Snel antwoord"; "action_quote" = "Citeren"; "action_react" = "Reageren"; -"action_reject" = "Reject"; +"action_reject" = "Weiger"; "action_remove" = "Verwijderen"; "action_reply" = "Antwoorden"; "action_reply_in_thread" = "Antwoord in subchat"; @@ -235,7 +235,7 @@ "common.do_not_show_this_again" = "Do not show this again"; "common.open_source_licenses" = "Open source licenses"; "common.pinned" = "Pinned"; -"common.send_to" = "Send to"; +"common.send_to" = "Sturen naar"; "common.you" = "You"; "confirm_recovery_key_banner_message" = "Je chatback-up is momenteel niet gesynchroniseerd. Je moet je herstelsleutel invoeren om toegang te behouden tot je chatback-up."; "confirm_recovery_key_banner_title" = "Voer je herstelsleutel in"; @@ -278,7 +278,7 @@ "event_shield_reason_unsigned_device" = "Encrypted by a device not verified by its owner."; "event_shield_reason_unverified_identity" = "Encrypted by an unverified user."; "full_screen_intent_banner_message" = "To ensure you never miss an important call, please change your settings to allow full-screen notifications when your phone is locked."; -"full_screen_intent_banner_title" = "Enhance your call experience"; +"full_screen_intent_banner_title" = "Verbeter je gesprekservaring"; "invite_friends_rich_title" = "🔐️ Sluit je bij mij aan op %1$@"; "invite_friends_text" = "Hé, praat met me op %1$@: %2$@"; "leave_conversation_alert_subtitle" = "Weet je zeker dat je dit gesprek wilt verlaten? Dit gesprek is niet openbaar en je kunt niet opnieuw deelnemen zonder een uitnodiging."; @@ -289,9 +289,9 @@ "notification_channel_call" = "Bellen"; "notification_channel_listening_for_events" = "Wachten op gebeurtenissen"; "notification_channel_noisy" = "Luide meldingen"; -"notification_channel_ringing_calls" = "Ringing calls"; +"notification_channel_ringing_calls" = "Overgaande oproepen"; "notification_channel_silent" = "Stille meldingen"; -"notification_incoming_call" = "Incoming call"; +"notification_incoming_call" = "Inkomende oproep"; "notification_inline_reply_failed" = "** Verzenden is mislukt - open de kamer"; "notification_invite_body" = "Nodigde je uit om te chatten"; "notification_invite_body_with_sender" = "%1$@ invited you to chat"; @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "Aangepaste basis-URL voor Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Stel een aangepaste basis-URL in voor Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Ongeldige URL, zorg ervoor dat je het protocol (http/https) en het juiste adres invult."; +"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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -477,7 +482,7 @@ "screen_edit_profile_updating_details" = "Profiel bijwerken…"; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; +"screen_encryption_reset_bullet_2" = "You will lose any message history that’s stored only on the server"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -570,24 +575,25 @@ "screen_qr_code_login_connection_note_secure_state_title" = "Verbinding niet veilig"; "screen_qr_code_login_device_code_subtitle" = "Daar word je gevraagd om de twee cijfers in te voeren die op dit apparaat worden weergegeven."; "screen_qr_code_login_device_code_title" = "Voer het onderstaande nummer in op je andere apparaat"; -"screen_qr_code_login_device_not_signed_in_scan_state_description" = "Sign in to your other device and then try again, or use another device that’s already signed in."; -"screen_qr_code_login_device_not_signed_in_scan_state_subtitle" = "Other device not signed in"; +"screen_qr_code_login_device_not_signed_in_scan_state_description" = "Log in op een ander apparaat en probeer opnieuw, of gebruik een ander apparaat dat al is ingelogd."; +"screen_qr_code_login_device_not_signed_in_scan_state_subtitle" = "Ander apparaat is niet ingelogd"; "screen_qr_code_login_error_cancelled_subtitle" = "De aanmelding is geannuleerd op het andere apparaat."; "screen_qr_code_login_error_cancelled_title" = "Login verzoek geannuleerd"; "screen_qr_code_login_error_declined_subtitle" = "De aanmelding is geweigerd op het andere apparaat."; "screen_qr_code_login_error_declined_title" = "Aanmelden geweigerd"; "screen_qr_code_login_error_expired_subtitle" = "Aanmelden is verlopen. Probeer het opnieuw."; "screen_qr_code_login_error_expired_title" = "De aanmelding was niet op tijd voltooid"; -"screen_qr_code_login_error_linking_not_suported_subtitle" = "Your other device does not support signing in to %@ with a QR code.\n\nTry signing in manually, or scan the QR code with another device."; +"screen_qr_code_login_error_linking_not_suported_subtitle" = "Jouw andere apparaat ondersteunt geen inloggen op %@ met een QR code.\n\nProbeer handmatig in te loggen, of scan de QR code met een ander apparaat."; "screen_qr_code_login_error_linking_not_suported_title" = "QR-code wordt niet ondersteund"; -"screen_qr_code_login_error_sliding_sync_not_supported_subtitle" = "Your account provider does not support %1$@."; -"screen_qr_code_login_error_sliding_sync_not_supported_title" = "%1$@ not supported"; +"screen_qr_code_login_error_sliding_sync_not_supported_subtitle" = "Je accountprovider ondersteunt geen %1$@."; +"screen_qr_code_login_error_sliding_sync_not_supported_title" = "%1$@ wordt niet ondersteund"; "screen_qr_code_login_initial_state_button_title" = "Klaar om te scannen"; "screen_qr_code_login_initial_state_item_1" = "Open %1$@ op een desktopapparaat"; "screen_qr_code_login_initial_state_item_2" = "Klik op je afbeelding"; "screen_qr_code_login_initial_state_item_3" = "Selecteer %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Nieuw apparaat koppelen”"; "screen_qr_code_login_initial_state_item_4" = "Scan de QR-code met dit apparaat"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Open %1$@ op een ander apparaat om de QR-code te krijgen"; "screen_qr_code_login_invalid_scan_state_description" = "Gebruik de QR-code die op het andere apparaat wordt weergegeven."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Verkeerde QR-code"; @@ -954,7 +960,7 @@ "dialog_title_success" = "Geslaagd"; "notification_fallback_content" = "Melding"; "notification_invitation_action_join" = "Deelnemen"; -"notification_invitation_action_reject" = "Reject"; +"notification_invitation_action_reject" = "Weiger"; "notification_room_action_mark_as_read" = "Markeren als gelezen"; "notification_room_action_quick_reply" = "Snel antwoord"; "screen_pinned_timeline_screen_title_empty" = "Pinned messages"; diff --git a/ElementX/Resources/Localizations/pl.lproj/Localizable.strings b/ElementX/Resources/Localizations/pl.lproj/Localizable.strings index b7fdaff6f5..cc8aa07e2f 100644 --- a/ElementX/Resources/Localizations/pl.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pl.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "Własny bazowy URL dla połączeń Element"; "screen_advanced_settings_element_call_base_url_description" = "Ustaw własny bazowy URL dla połączeń Element"; "screen_advanced_settings_element_call_base_url_validation_error" = "Nieprawidłowy adres URL, upewnij się, że zawiera protokół (http/https) i poprawny adres."; +"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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Naciśnij wiadomość i wybierz “%1$@”, aby dołączyć tutaj."; "screen_pinned_timeline_empty_state_headline" = "Przypinaj ważne wiadomości, aby można było je łatwo znaleźć"; "screen_reset_encryption_password_error" = "Wystąpił nieznany błąd. Sprawdź, czy hasło jest poprawne i spróbuj ponownie."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Wybierz %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Powiąż nowe urządzenie”"; "screen_qr_code_login_initial_state_item_4" = "Zeskanuj kod QR za pomocą tego urządzenia"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Otwórz %1$@ na innym urządzeniu, aby uzyskać kod QR"; "screen_qr_code_login_invalid_scan_state_description" = "Użyj kodu QR widocznego na drugim urządzeniu."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Błędny kod QR"; diff --git a/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings b/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings index 7435f5a751..7d1e3d7c2d 100644 --- a/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings @@ -334,6 +334,11 @@ "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" = "URL inválida, por favor verifique se o protocolo (http/https) e o endereço correto estão presentes."; +"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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -477,7 +482,7 @@ "screen_edit_profile_updating_details" = "Atualizando o perfil..."; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; +"screen_encryption_reset_bullet_2" = "You will lose any message history that’s stored only on the server"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Select %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Link new device”"; "screen_qr_code_login_initial_state_item_4" = "Scan the QR code with this device"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Open %1$@ on another device to get the QR code"; "screen_qr_code_login_invalid_scan_state_description" = "Use the QR code shown on the other device."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Wrong QR code"; diff --git a/ElementX/Resources/Localizations/pt.lproj/Localizable.strings b/ElementX/Resources/Localizations/pt.lproj/Localizable.strings index de2820b436..2210564d29 100644 --- a/ElementX/Resources/Localizations/pt.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pt.lproj/Localizable.strings @@ -273,7 +273,7 @@ "error_unknown" = "Ocorreu um erro, desculpa"; "event_shield_reason_authenticity_not_guaranteed" = "A autenticidade desta mensagem cifrada não pode ser garantida neste dispositivo."; "event_shield_reason_previously_verified" = "Criptografado por um usuário verificado anteriormente."; -"event_shield_reason_sent_in_clear" = "Não encriptado."; +"event_shield_reason_sent_in_clear" = "Não cifrado."; "event_shield_reason_unknown_device" = "Cifragem com origem num dispositivo eliminado ou desconhecido."; "event_shield_reason_unsigned_device" = "Cifragem com origem num dispositivo não verificado pelo seu dono."; "event_shield_reason_unverified_identity" = "Cifragem com origem num utilizador não verificado."; @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "URL base para Element Call personalizado"; "screen_advanced_settings_element_call_base_url_description" = "Define um URL base para a Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL inválido, certifica-te de que incluis o protocolo (http/https) e o endereço correto."; +"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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Pressione uma mensagem e escolha \"%1$@\" para incluir aqui."; "screen_pinned_timeline_empty_state_headline" = "Fixa mensagens importantes para que possam ser facilmente descobertas"; "screen_reset_encryption_password_error" = "Um erro desconhecido aconteceu. Verifique se a senha da sua conta está correta e tente novamente."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Seleciona %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Ligar novo dispositivo”"; "screen_qr_code_login_initial_state_item_4" = "Lê o código QR com este dispositivo"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Abre a %1$@ noutro dispositivo para obteres o código QR"; "screen_qr_code_login_invalid_scan_state_description" = "Lê o código QR apresentado no outro dispositivo."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Código QR inválido"; diff --git a/ElementX/Resources/Localizations/ro.lproj/Localizable.strings b/ElementX/Resources/Localizations/ro.lproj/Localizable.strings index 93ee5c9832..2e86193909 100644 --- a/ElementX/Resources/Localizations/ro.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ro.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "Adresa URL de bază Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Setați o adresă URL de bază personalizată pentru Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL invalid, vă rugăm să vă asigurați că includeți protocolul (http/https) și adresa corectă."; +"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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -477,7 +482,7 @@ "screen_edit_profile_updating_details" = "Se actualizează profilul..."; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; +"screen_encryption_reset_bullet_2" = "You will lose any message history that’s stored only on the server"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Selectați %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "„Conectați un dispozitiv nou”"; "screen_qr_code_login_initial_state_item_4" = "Scanați codul QR cu acest dispozitiv"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Deschideți %1$@ pe un alt dispozitiv pentru a obține codul QR"; "screen_qr_code_login_invalid_scan_state_description" = "Utilizați codul QR afișat pe celălalt dispozitiv."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Cod QR greșit"; diff --git a/ElementX/Resources/Localizations/ru.lproj/Localizable.strings b/ElementX/Resources/Localizations/ru.lproj/Localizable.strings index 6841ffb00d..d9d0f216f4 100644 --- a/ElementX/Resources/Localizations/ru.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ru.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "Базовый URL сервера звонков Element"; "screen_advanced_settings_element_call_base_url_description" = "Задайте свой сервер Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Адрес указан неверно, удостоверьтесь, что вы указали протокол (http/https) и правильный адрес."; +"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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Нажмите на сообщение и выберите “%1$@”, чтобы добавить его сюда."; "screen_pinned_timeline_empty_state_headline" = "Закрепите важные сообщения, чтобы их можно было легко найти"; "screen_reset_encryption_password_error" = "Произошла неизвестная ошибка. Проверьте правильность пароля учетной записи и повторите попытку."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Выбрать %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "\"Привязать новое устройство\""; "screen_qr_code_login_initial_state_item_4" = "Отсканируйте QR-код с помощью этого устройства"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Откройте %1$@ на другом устройстве, чтобы получить QR-код"; "screen_qr_code_login_invalid_scan_state_description" = "Используйте QR-код, показанный на другом устройстве."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Неверный QR-код"; diff --git a/ElementX/Resources/Localizations/sk.lproj/Localizable.strings b/ElementX/Resources/Localizations/sk.lproj/Localizable.strings index d982ecb5bb..ca74610ed9 100644 --- a/ElementX/Resources/Localizations/sk.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/sk.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "Vlastná Element Call základná URL adresa"; "screen_advanced_settings_element_call_base_url_description" = "Nastaviť vlastnú základnú URL adresu pre Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Neplatná adresa URL, uistite sa, že ste uviedli protokol (http/https) a správnu adresu."; +"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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Stlačte správu a vyberte možnosť „%1$@“, ktorú chcete zahrnúť sem."; "screen_pinned_timeline_empty_state_headline" = "Pripnite dôležité správy, aby sa dali ľahko nájsť"; "screen_reset_encryption_password_error" = "Nastala neznáma chyba. Skontrolujte, či je heslo vášho účtu správne a skúste to znova."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Vyberte %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "„Prepojiť nové zariadenie“"; "screen_qr_code_login_initial_state_item_4" = "Naskenujte QR kód pomocou tohto zariadenia"; +"screen_qr_code_login_initial_state_subtitle" = "Dostupné iba v prípade, že to podporuje váš poskytovateľ účtu."; "screen_qr_code_login_initial_state_title" = "Ak chcete získať QR kód, otvorte %1$@ na inom zariadení"; "screen_qr_code_login_invalid_scan_state_description" = "Použite QR kód zobrazený na druhom zariadení."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Nesprávny QR kód"; diff --git a/ElementX/Resources/Localizations/sv.lproj/Localizable.strings b/ElementX/Resources/Localizations/sv.lproj/Localizable.strings index b8a62974c3..0609073b10 100644 --- a/ElementX/Resources/Localizations/sv.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/sv.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "Anpassad bas-URL för Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Ange en anpassad bas-URL för Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Ogiltig URL, se till att du inkluderar protokollet (http/https) och rätt adress."; +"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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Tryck på ett meddelande och välj ”%1$@” för att inkludera det här."; "screen_pinned_timeline_empty_state_headline" = "Fäst viktiga meddelanden så att de lätt kan upptäckas"; "screen_reset_encryption_password_error" = "Ett okänt fel inträffade. Kontrollera att ditt kontolösenord är korrekt och försök igen."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Välj %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "”Länka ny enhet”"; "screen_qr_code_login_initial_state_item_4" = "Skanna QR-koden med den här enheten"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Öppna %1$@ på en annan enhet för att få QR-koden"; "screen_qr_code_login_invalid_scan_state_description" = "Använd QR-koden som visas på den andra enheten."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Fel QR-kod"; diff --git a/ElementX/Resources/Localizations/uk.lproj/Localizable.strings b/ElementX/Resources/Localizations/uk.lproj/Localizable.strings index c237425ac4..0c8ac30427 100644 --- a/ElementX/Resources/Localizations/uk.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/uk.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "Користувацька URL-адреса Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Встановіть URL-адресу для Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Неправильна URL-адреса, будь ласка, переконайтеся, що ви вказали протокол (http/https) та правильну адресу."; +"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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Оберіть %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Підключити новий пристрій”"; "screen_qr_code_login_initial_state_item_4" = "Відскануйте QR-код цим пристроєм"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Відкрийте %1$@ на іншому пристрої, щоб отримати QR-код"; "screen_qr_code_login_invalid_scan_state_description" = "Використовуйте QR-код, показаний на іншому пристрої."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Неправильний QR-код"; diff --git a/ElementX/Resources/Localizations/uz.lproj/Localizable.strings b/ElementX/Resources/Localizations/uz.lproj/Localizable.strings index a41ecfc4e9..2d89505b08 100644 --- a/ElementX/Resources/Localizations/uz.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/uz.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "Maxsus element qo‘ng‘iroqlar bazasi URL manzili"; "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_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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -477,7 +482,7 @@ "screen_edit_profile_updating_details" = "Profil yangilanmoqda…"; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; +"screen_encryption_reset_bullet_2" = "You will lose any message history that’s stored only on the server"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Select %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Link new device”"; "screen_qr_code_login_initial_state_item_4" = "Scan the QR code with this device"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Open %1$@ on another device to get the QR code"; "screen_qr_code_login_invalid_scan_state_description" = "Use the QR code shown on the other device."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Wrong QR code"; diff --git a/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings b/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings index 4ffda912d5..ee790a99d5 100644 --- a/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings @@ -334,6 +334,11 @@ "screen_advanced_settings_element_call_base_url" = "自定义 Element Call URL"; "screen_advanced_settings_element_call_base_url_description" = "为 Element 通话设置根 URL。"; "screen_advanced_settings_element_call_base_url_validation_error" = "URL 无效,请确保包含协议(http/https)和正确的地址。"; +"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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "按下消息并选择 “%1$@” 将其包含在此处。"; "screen_pinned_timeline_empty_state_headline" = "固定重要消息,以便轻松发现它们"; "screen_reset_encryption_password_error" = "发生未知错误。请检查您的帐户密码是否正确,然后重试。"; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "选择 %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "「连接新设备」"; "screen_qr_code_login_initial_state_item_4" = "使用此设备扫描二维码"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "在另一台设备上打开 %1$@ 以获取二维码"; "screen_qr_code_login_invalid_scan_state_description" = "使用其他设备上显示的二维码。"; "screen_qr_code_login_invalid_scan_state_subtitle" = "二维码错误"; diff --git a/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings b/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings index 5aaedaacaf..a085b2c6a1 100644 --- a/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings @@ -334,6 +334,11 @@ "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_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"; +"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; +"screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -477,7 +482,7 @@ "screen_edit_profile_updating_details" = "正在更新個人檔案..."; "screen_encryption_reset_action_continue_reset" = "Continue reset"; "screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose your existing message history unless it is stored on another device"; +"screen_encryption_reset_bullet_2" = "You will lose any message history that’s stored only on the server"; "screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; "screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; "screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; @@ -588,6 +593,7 @@ "screen_qr_code_login_initial_state_item_3" = "Select %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Link new device”"; "screen_qr_code_login_initial_state_item_4" = "Scan the QR code with this device"; +"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; "screen_qr_code_login_initial_state_title" = "Open %1$@ on another device to get the QR code"; "screen_qr_code_login_invalid_scan_state_description" = "Use the QR code shown on the other device."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Wrong QR code"; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 14ddac8081..ec9b1c81e2 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -1043,6 +1043,16 @@ internal enum L10n { internal static var screenCreatePollQuestionHint: String { return L10n.tr("Localizable", "screen_create_poll_question_hint") } /// Create Poll internal static var screenCreatePollTitle: String { return L10n.tr("Localizable", "screen_create_poll_title") } + /// Anyone can join this room + internal static var screenCreateRoomAccessSectionAnyoneOptionDescription: String { return L10n.tr("Localizable", "screen_create_room_access_section_anyone_option_description") } + /// Anyone + internal static var screenCreateRoomAccessSectionAnyoneOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_access_section_anyone_option_title") } + /// Room Access + internal static var screenCreateRoomAccessSectionHeader: String { return L10n.tr("Localizable", "screen_create_room_access_section_header") } + /// Anyone can ask to join the room but an administrator or a moderator will have to accept the request + internal static var screenCreateRoomAccessSectionKnockingOptionDescription: String { return L10n.tr("Localizable", "screen_create_room_access_section_knocking_option_description") } + /// Ask to join + internal static var screenCreateRoomAccessSectionKnockingOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_access_section_knocking_option_title") } /// New room internal static var screenCreateRoomActionCreateRoom: String { return L10n.tr("Localizable", "screen_create_room_action_create_room") } /// Invite people From a16e1346fe2209bbafb4218f5c7adf7a9e27fc7b Mon Sep 17 00:00:00 2001 From: Mauro <34335419+Velin92@users.noreply.github.com> Date: Mon, 14 Oct 2024 11:52:38 +0200 Subject: [PATCH 052/114] Create Room with knock rule (#3397) * create knock room implementation (without SDK) * Apply suggestions from code review pr suggestions Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com> * pr suggestions --------- Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com> --- .../Sources/Application/AppSettings.swift | 4 ++ ElementX/Sources/Mocks/ClientProxyMock.swift | 2 +- .../Mocks/Generated/GeneratedMocks.swift | 48 +++++++++---------- .../CreateRoom/CreateRoomCoordinator.swift | 3 +- .../Screens/CreateRoom/CreateRoomModels.swift | 2 + .../CreateRoom/CreateRoomViewModel.swift | 22 ++++++--- .../CreateRoom/View/CreateRoomScreen.swift | 41 +++++++++++++++- .../DeveloperOptionsScreenModels.swift | 1 + .../View/DeveloperOptionsScreen.swift | 7 +++ .../Sources/Services/Client/ClientProxy.swift | 3 +- .../Services/Client/ClientProxyProtocol.swift | 2 +- .../CreateRoom/CreateRoomFlowParameters.swift | 1 + ...eateRoom-iPad-en-GB.Create-Public-Room.png | 3 ++ ...ateRoom-iPad-pseudo.Create-Public-Room.png | 3 ++ ...oom-iPhone-16-en-GB.Create-Public-Room.png | 3 ++ ...om-iPhone-16-pseudo.Create-Public-Room.png | 3 ++ .../Sources/CreateRoomViewModelTests.swift | 38 ++++++++++++++- 17 files changed, 149 insertions(+), 37 deletions(-) create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Public-Room.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Public-Room.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Public-Room.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Public-Room.png diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index 66a4ef7831..1f97ab1f36 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -47,6 +47,7 @@ final class AppSettings { case pinningEnabled case enableOnlySignedDeviceIsolationMode case identityPinningViolationNotificationsEnabled + case knockingEnabled } private static var suiteName: String = InfoPlistReader.main.appGroupIdentifier @@ -282,6 +283,9 @@ final class AppSettings { @UserPreference(key: UserDefaultsKeys.identityPinningViolationNotificationsEnabled, defaultValue: isDevelopmentBuild, storageType: .userDefaults(store)) var identityPinningViolationNotificationsEnabled + + @UserPreference(key: UserDefaultsKeys.knockingEnabled, defaultValue: false, storageType: .userDefaults(store)) + var knockingEnabled #endif diff --git a/ElementX/Sources/Mocks/ClientProxyMock.swift b/ElementX/Sources/Mocks/ClientProxyMock.swift index 825588e479..507a21b0a9 100644 --- a/ElementX/Sources/Mocks/ClientProxyMock.swift +++ b/ElementX/Sources/Mocks/ClientProxyMock.swift @@ -50,7 +50,7 @@ extension ClientProxyMock { canDeactivateAccount = false directRoomForUserIDReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) createDirectRoomWithExpectedRoomNameReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) - createRoomNameTopicIsRoomPrivateUserIDsAvatarURLReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) + createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) uploadMediaReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) loadUserDisplayNameReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) setUserDisplayNameReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index c9f2da6433..db66e04c9c 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -2627,15 +2627,15 @@ class ClientProxyMock: ClientProxyProtocol { } //MARK: - createRoom - var createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingCallsCount = 0 - var createRoomNameTopicIsRoomPrivateUserIDsAvatarURLCallsCount: Int { + var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingCallsCount = 0 + var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLCallsCount: Int { get { if Thread.isMainThread { - return createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingCallsCount + return createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingCallsCount + returnValue = createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingCallsCount } return returnValue! @@ -2643,29 +2643,29 @@ class ClientProxyMock: ClientProxyProtocol { } set { if Thread.isMainThread { - createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingCallsCount = newValue + createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingCallsCount = newValue + createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingCallsCount = newValue } } } } - var createRoomNameTopicIsRoomPrivateUserIDsAvatarURLCalled: Bool { - return createRoomNameTopicIsRoomPrivateUserIDsAvatarURLCallsCount > 0 + var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLCalled: Bool { + return createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLCallsCount > 0 } - var createRoomNameTopicIsRoomPrivateUserIDsAvatarURLReceivedArguments: (name: String, topic: String?, isRoomPrivate: Bool, userIDs: [String], avatarURL: URL?)? - var createRoomNameTopicIsRoomPrivateUserIDsAvatarURLReceivedInvocations: [(name: String, topic: String?, isRoomPrivate: Bool, userIDs: [String], avatarURL: URL?)] = [] + var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLReceivedArguments: (name: String, topic: String?, isRoomPrivate: Bool, isKnockingOnly: Bool, userIDs: [String], avatarURL: URL?)? + var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLReceivedInvocations: [(name: String, topic: String?, isRoomPrivate: Bool, isKnockingOnly: Bool, userIDs: [String], avatarURL: URL?)] = [] - var createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingReturnValue: Result! - var createRoomNameTopicIsRoomPrivateUserIDsAvatarURLReturnValue: Result! { + var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingReturnValue: Result! + var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLReturnValue: Result! { get { if Thread.isMainThread { - return createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingReturnValue + return createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingReturnValue } else { var returnValue: Result? = nil DispatchQueue.main.sync { - returnValue = createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingReturnValue + returnValue = createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingReturnValue } return returnValue! @@ -2673,26 +2673,26 @@ class ClientProxyMock: ClientProxyProtocol { } set { if Thread.isMainThread { - createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingReturnValue = newValue + createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingReturnValue = newValue + createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingReturnValue = newValue } } } } - var createRoomNameTopicIsRoomPrivateUserIDsAvatarURLClosure: ((String, String?, Bool, [String], URL?) async -> Result)? + var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLClosure: ((String, String?, Bool, Bool, [String], URL?) async -> Result)? - func createRoom(name: String, topic: String?, isRoomPrivate: Bool, userIDs: [String], avatarURL: URL?) async -> Result { - createRoomNameTopicIsRoomPrivateUserIDsAvatarURLCallsCount += 1 - createRoomNameTopicIsRoomPrivateUserIDsAvatarURLReceivedArguments = (name: name, topic: topic, isRoomPrivate: isRoomPrivate, userIDs: userIDs, avatarURL: avatarURL) + func createRoom(name: String, topic: String?, isRoomPrivate: Bool, isKnockingOnly: Bool, userIDs: [String], avatarURL: URL?) async -> Result { + createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLCallsCount += 1 + createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLReceivedArguments = (name: name, topic: topic, isRoomPrivate: isRoomPrivate, isKnockingOnly: isKnockingOnly, userIDs: userIDs, avatarURL: avatarURL) DispatchQueue.main.async { - self.createRoomNameTopicIsRoomPrivateUserIDsAvatarURLReceivedInvocations.append((name: name, topic: topic, isRoomPrivate: isRoomPrivate, userIDs: userIDs, avatarURL: avatarURL)) + self.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLReceivedInvocations.append((name: name, topic: topic, isRoomPrivate: isRoomPrivate, isKnockingOnly: isKnockingOnly, userIDs: userIDs, avatarURL: avatarURL)) } - if let createRoomNameTopicIsRoomPrivateUserIDsAvatarURLClosure = createRoomNameTopicIsRoomPrivateUserIDsAvatarURLClosure { - return await createRoomNameTopicIsRoomPrivateUserIDsAvatarURLClosure(name, topic, isRoomPrivate, userIDs, avatarURL) + if let createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLClosure = createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLClosure { + return await createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLClosure(name, topic, isRoomPrivate, isKnockingOnly, userIDs, avatarURL) } else { - return createRoomNameTopicIsRoomPrivateUserIDsAvatarURLReturnValue + return createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLReturnValue } } //MARK: - joinRoom diff --git a/ElementX/Sources/Screens/CreateRoom/CreateRoomCoordinator.swift b/ElementX/Sources/Screens/CreateRoom/CreateRoomCoordinator.swift index da965f2e2f..a37163f97c 100644 --- a/ElementX/Sources/Screens/CreateRoom/CreateRoomCoordinator.swift +++ b/ElementX/Sources/Screens/CreateRoom/CreateRoomCoordinator.swift @@ -37,7 +37,8 @@ final class CreateRoomCoordinator: CoordinatorProtocol { createRoomParameters: parameters.createRoomParameters, selectedUsers: parameters.selectedUsers, analytics: ServiceLocator.shared.analytics, - userIndicatorController: parameters.userIndicatorController) + userIndicatorController: parameters.userIndicatorController, + appSettings: ServiceLocator.shared.settings) } func start() { diff --git a/ElementX/Sources/Screens/CreateRoom/CreateRoomModels.swift b/ElementX/Sources/Screens/CreateRoom/CreateRoomModels.swift index 87ef365a15..44cefd052c 100644 --- a/ElementX/Sources/Screens/CreateRoom/CreateRoomModels.swift +++ b/ElementX/Sources/Screens/CreateRoom/CreateRoomModels.swift @@ -25,6 +25,7 @@ enum CreateRoomViewModelAction { } struct CreateRoomViewState: BindableState { + let isKnockingFeatureEnabled: Bool var selectedUsers: [UserProfileProxy] var bindings: CreateRoomViewStateBindings var avatarURL: URL? @@ -37,6 +38,7 @@ struct CreateRoomViewStateBindings { var roomName: String var roomTopic: String var isRoomPrivate: Bool + var isKnockingOnly = false var showAttachmentConfirmationDialog = false /// Information describing the currently displayed alert. diff --git a/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift b/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift index e8d4e2da90..39fde2fcda 100644 --- a/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift +++ b/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift @@ -26,7 +26,8 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol createRoomParameters: CurrentValuePublisher, selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never>, analytics: AnalyticsService, - userIndicatorController: UserIndicatorControllerProtocol) { + userIndicatorController: UserIndicatorControllerProtocol, + appSettings: AppSettings) { let parameters = createRoomParameters.value self.userSession = userSession @@ -36,7 +37,7 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol let bindings = CreateRoomViewStateBindings(roomName: parameters.name, roomTopic: parameters.topic, isRoomPrivate: parameters.isRoomPrivate) - super.init(initialViewState: CreateRoomViewState(selectedUsers: selectedUsers.value, bindings: bindings), mediaProvider: userSession.mediaProvider) + super.init(initialViewState: CreateRoomViewState(isKnockingFeatureEnabled: appSettings.knockingEnabled, selectedUsers: selectedUsers.value, bindings: bindings), mediaProvider: userSession.mediaProvider) createRoomParameters .map(\.avatarImageMedia) @@ -92,21 +93,28 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol old.roomName == new.roomName && old.roomTopic == new.roomTopic && old.isRoomPrivate == new.isRoomPrivate } .sink { [weak self] bindings in - guard let self else { return } - createRoomParameters.name = bindings.roomName - createRoomParameters.topic = bindings.roomTopic - createRoomParameters.isRoomPrivate = bindings.isRoomPrivate + guard let self = self else { return } + updateParameters(bindings: bindings) actionsSubject.send(.updateDetails(createRoomParameters)) } .store(in: &cancellables) } + private func updateParameters(bindings: CreateRoomViewStateBindings) { + createRoomParameters.name = bindings.roomName + createRoomParameters.topic = bindings.roomTopic + createRoomParameters.isRoomPrivate = bindings.isRoomPrivate + createRoomParameters.isKnockingOnly = bindings.isKnockingOnly + } + private func createRoom() async { defer { hideLoadingIndicator() } showLoadingIndicator() + // Since the parameters are throttled, we need to make sure that the latest values are used + updateParameters(bindings: state.bindings) let avatarURL: URL? if let media = createRoomParameters.avatarImageMedia { switch await userSession.clientProxy.uploadMedia(media) { @@ -136,6 +144,8 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol switch await userSession.clientProxy.createRoom(name: createRoomParameters.name, topic: createRoomParameters.topic, isRoomPrivate: createRoomParameters.isRoomPrivate, + // As of right now we don't want to make private rooms with the knock rule + isKnockingOnly: createRoomParameters.isRoomPrivate ? false : createRoomParameters.isKnockingOnly, userIDs: state.selectedUsers.map(\.userID), avatarURL: avatarURL) { case .success(let roomId): diff --git a/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift b/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift index 32eb00b9e1..726cf99f0e 100644 --- a/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift +++ b/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift @@ -22,6 +22,10 @@ struct CreateRoomScreen: View { roomSection topicSection securitySection + if context.viewState.isKnockingFeatureEnabled, + !context.isRoomPrivate { + roomAccessSection + } } .compoundList() .track(screen: .CreateRoom) @@ -151,6 +155,20 @@ struct CreateRoomScreen: View { } } + private var roomAccessSection: some View { + Section { + ListRow(label: .plain(title: L10n.screenCreateRoomAccessSectionAnyoneOptionTitle, + description: L10n.screenCreateRoomAccessSectionAnyoneOptionDescription), + kind: .selection(isSelected: !context.isKnockingOnly) { context.isKnockingOnly = false }) + ListRow(label: .plain(title: L10n.screenCreateRoomAccessSectionKnockingOptionTitle, + description: L10n.screenCreateRoomAccessSectionKnockingOptionDescription), + kind: .selection(isSelected: context.isKnockingOnly) { context.isKnockingOnly = true }) + } header: { + Text(L10n.screenCreateRoomAccessSectionHeader.uppercased()) + .compoundListSectionHeader() + } + } + private var toolbar: some ToolbarContent { ToolbarItem(placement: .confirmationAction) { Button(L10n.actionCreate) { @@ -174,7 +192,8 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview { createRoomParameters: .init(parameters), selectedUsers: .init(selectedUsers), analytics: ServiceLocator.shared.analytics, - userIndicatorController: UserIndicatorControllerMock()) + userIndicatorController: UserIndicatorControllerMock(), + appSettings: ServiceLocator.shared.settings) }() static let emtpyViewModel = { @@ -184,7 +203,21 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview { createRoomParameters: .init(parameters), selectedUsers: .init([]), analytics: ServiceLocator.shared.analytics, - userIndicatorController: UserIndicatorControllerMock()) + userIndicatorController: UserIndicatorControllerMock(), + appSettings: ServiceLocator.shared.settings) + }() + + static let publicRoomViewModel = { + let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com")))) + let parameters = CreateRoomFlowParameters(isRoomPrivate: false) + let selectedUsers: [UserProfileProxy] = [.mockAlice, .mockBob, .mockCharlie] + ServiceLocator.shared.settings.knockingEnabled = true + return CreateRoomViewModel(userSession: userSession, + createRoomParameters: .init(parameters), + selectedUsers: .init([]), + analytics: ServiceLocator.shared.analytics, + userIndicatorController: UserIndicatorControllerMock(), + appSettings: ServiceLocator.shared.settings) }() static var previews: some View { @@ -196,5 +229,9 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview { CreateRoomScreen(context: emtpyViewModel.context) } .previewDisplayName("Create Room without users") + NavigationStack { + CreateRoomScreen(context: publicRoomViewModel.context) + } + .previewDisplayName("Create Public Room") } } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift index 4bff8d3690..7e186b3a05 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift @@ -49,6 +49,7 @@ protocol DeveloperOptionsProtocol: AnyObject { var enableOnlySignedDeviceIsolationMode: Bool { get set } var elementCallBaseURLOverride: URL? { get set } var identityPinningViolationNotificationsEnabled: Bool { get set } + var knockingEnabled: Bool { get set } } extension AppSettings: DeveloperOptionsProtocol { } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift index df1afd5400..42a3000faa 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -55,6 +55,13 @@ struct DeveloperOptionsScreen: View { } } + Section("Join rules") { + Toggle(isOn: $context.knockingEnabled) { + Text("Knocking") + Text("Experimental, still using mocked data") + } + } + Section { Toggle(isOn: $context.enableOnlySignedDeviceIsolationMode) { Text("Exclude insecure devices when sending/receiving messages") diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 8ed91d96c4..32fdf374ab 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -365,8 +365,9 @@ class ClientProxy: ClientProxyProtocol { } } - func createRoom(name: String, topic: String?, isRoomPrivate: Bool, userIDs: [String], avatarURL: URL?) async -> Result { + func createRoom(name: String, topic: String?, isRoomPrivate: Bool, isKnockingOnly: Bool, userIDs: [String], avatarURL: URL?) async -> Result { do { + // TODO: Revisit once the SDK supports the knocking API let parameters = CreateRoomParameters(name: name, topic: topic, isEncrypted: isRoomPrivate, diff --git a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift index a942ec638e..0726ba83e6 100644 --- a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift +++ b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift @@ -138,7 +138,7 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol { func createDirectRoom(with userID: String, expectedRoomName: String?) async -> Result - func createRoom(name: String, topic: String?, isRoomPrivate: Bool, userIDs: [String], avatarURL: URL?) async -> Result + func createRoom(name: String, topic: String?, isRoomPrivate: Bool, isKnockingOnly: Bool, userIDs: [String], avatarURL: URL?) async -> Result func joinRoom(_ roomID: String, via: [String]) async -> Result diff --git a/ElementX/Sources/Services/CreateRoom/CreateRoomFlowParameters.swift b/ElementX/Sources/Services/CreateRoom/CreateRoomFlowParameters.swift index d61a7e79ea..d2e03d96dd 100644 --- a/ElementX/Sources/Services/CreateRoom/CreateRoomFlowParameters.swift +++ b/ElementX/Sources/Services/CreateRoom/CreateRoomFlowParameters.swift @@ -12,5 +12,6 @@ struct CreateRoomFlowParameters { var name = "" var topic = "" var isRoomPrivate = true + var isKnockingOnly = false var avatarImageMedia: MediaInfo? } diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Public-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Public-Room.png new file mode 100644 index 0000000000..97c0f2fdb3 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Public-Room.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2b6822fdd0ddc3b3c87d0142377a0951e9478c2fb1a1b840204589b2bc6270e +size 175831 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Public-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Public-Room.png new file mode 100644 index 0000000000..4a25b1b65f --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Public-Room.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1b805b55944beb58caccd5dbf4e98261582fe006ce8c08014bfaf26c13eea75f +size 211876 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Public-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Public-Room.png new file mode 100644 index 0000000000..2106fa84dd --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Public-Room.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0fccdc54a2d88e39353bdb49f32040749072dd17ac3c6cacc165ab6f0812d500 +size 123697 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Public-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Public-Room.png new file mode 100644 index 0000000000..712e0347aa --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Public-Room.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35ecf1e814eedd5f2d388224aee6fc885b1d9e1fe174b9199d9fe7bb9004a355 +size 164558 diff --git a/UnitTests/Sources/CreateRoomViewModelTests.swift b/UnitTests/Sources/CreateRoomViewModelTests.swift index 2dc1940e20..b145dfbfd9 100644 --- a/UnitTests/Sources/CreateRoomViewModelTests.swift +++ b/UnitTests/Sources/CreateRoomViewModelTests.swift @@ -29,11 +29,13 @@ class CreateRoomScreenViewModelTests: XCTestCase { userSession = UserSessionMock(.init(clientProxy: clientProxy)) let parameters = CreateRoomFlowParameters() usersSubject.send([.mockAlice, .mockBob, .mockCharlie]) + ServiceLocator.shared.settings.knockingEnabled = true let viewModel = CreateRoomViewModel(userSession: userSession, createRoomParameters: .init(parameters), selectedUsers: usersSubject.asCurrentValuePublisher(), analytics: ServiceLocator.shared.analytics, - userIndicatorController: UserIndicatorControllerMock()) + userIndicatorController: UserIndicatorControllerMock(), + appSettings: ServiceLocator.shared.settings) self.viewModel = viewModel viewModel.actions.sink { [weak self] action in @@ -69,4 +71,38 @@ class CreateRoomScreenViewModelTests: XCTestCase { context.roomName = "A" XCTAssertTrue(context.viewState.canCreateRoom) } + + func testCreateKnockingRoom() async { + context.roomName = "A" + context.roomTopic = "B" + context.isRoomPrivate = false + context.isKnockingOnly = true + XCTAssertTrue(context.viewState.canCreateRoom) + + let expectation = expectation(description: "Wait for the room to be created") + clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLClosure = { _, _, isPrivate, isKnockingOnly, _, _ in + XCTAssertTrue(isKnockingOnly) + XCTAssertFalse(isPrivate) + defer { expectation.fulfill() } + return .success("") + } + context.send(viewAction: .createRoom) + await fulfillment(of: [expectation]) + } + + func testCreatePrivateRoomCantHaveKnockRule() async { + context.roomName = "A" + context.roomTopic = "B" + context.isRoomPrivate = true + context.isKnockingOnly = true + context.send(viewAction: .createRoom) + let expectation = expectation(description: "Wait for the room to be created") + clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLClosure = { _, _, isPrivate, isKnockingOnly, _, _ in + XCTAssertFalse(isKnockingOnly) + XCTAssertTrue(isPrivate) + expectation.fulfill() + return .success("") + } + await fulfillment(of: [expectation]) + } } From b1b229797225c3384300ec0354fcc31587fe484b Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 14 Oct 2024 13:59:09 +0100 Subject: [PATCH 053/114] Update the SDK. (#3407) Fixes a bug where we couldn't open images sent by EXA with a mimetype of image/* --- ElementX.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- .../Sources/Mocks/EventTimelineItem.swift | 6 +- .../Mocks/Generated/SDKGeneratedMocks.swift | 460 ++++++++++-------- .../Sources/Services/Client/ClientProxy.swift | 2 +- .../Services/Media/Provider/MediaLoader.swift | 6 +- .../Services/Timeline/TimelineItemProxy.swift | 4 +- project.yml | 2 +- 8 files changed, 262 insertions(+), 224 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 866bf06098..e2195795a7 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7794,7 +7794,7 @@ repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.0.56; + version = 1.0.57; }; }; 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index a99fe114cf..efa35cd9e8 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/matrix-rust-components-swift", "state" : { - "revision" : "1366154c2e601179514be93e2fca48969c4f2ed8", - "version" : "1.0.56" + "revision" : "5f01589d44aa7045dc1ad87f2583f6083295d3eb", + "version" : "1.0.57" } }, { diff --git a/ElementX/Sources/Mocks/EventTimelineItem.swift b/ElementX/Sources/Mocks/EventTimelineItem.swift index 010e040a5c..229fe658ca 100644 --- a/ElementX/Sources/Mocks/EventTimelineItem.swift +++ b/ElementX/Sources/Mocks/EventTimelineItem.swift @@ -18,8 +18,7 @@ struct EventTimelineItemSDKMockConfiguration { extension EventTimelineItem { init(configuration: EventTimelineItemSDKMockConfiguration) { - self.init(isLocal: false, - isRemote: true, + self.init(isRemote: true, eventOrTransactionId: .eventId(eventId: configuration.eventID), sender: configuration.sender, senderProfile: .pending, @@ -28,12 +27,11 @@ extension EventTimelineItem { content: configuration.content, timestamp: 0, reactions: [], - debugInfoProvider: EventTimelineItemDebugInfoProviderSDKMock(), localSendState: nil, readReceipts: [:], origin: nil, canBeRepliedTo: false, - shieldsProvider: EventShieldsProviderSDKMock()) + lazyProvider: LazyTimelineItemProviderSDKMock()) } static var mockMessage: EventTimelineItem { diff --git a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift index da47bc6dc2..e4548ff0f1 100644 --- a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift @@ -1160,16 +1160,16 @@ open class ClientSDKMock: MatrixRustSDK.Client { //MARK: - getMediaFile - open var getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirThrowableError: Error? - var getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirUnderlyingCallsCount = 0 - open var getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirCallsCount: Int { + open var getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirThrowableError: Error? + var getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirUnderlyingCallsCount = 0 + open var getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirCallsCount: Int { get { if Thread.isMainThread { - return getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirUnderlyingCallsCount + return getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirUnderlyingCallsCount + returnValue = getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirUnderlyingCallsCount } return returnValue! @@ -1177,29 +1177,29 @@ open class ClientSDKMock: MatrixRustSDK.Client { } set { if Thread.isMainThread { - getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirUnderlyingCallsCount = newValue + getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirUnderlyingCallsCount = newValue + getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirUnderlyingCallsCount = newValue } } } } - open var getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirCalled: Bool { - return getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirCallsCount > 0 + open var getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirCalled: Bool { + return getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirCallsCount > 0 } - open var getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirReceivedArguments: (mediaSource: MediaSource, body: String?, mimeType: String, useCache: Bool, tempDir: String?)? - open var getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirReceivedInvocations: [(mediaSource: MediaSource, body: String?, mimeType: String, useCache: Bool, tempDir: String?)] = [] + open var getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirReceivedArguments: (mediaSource: MediaSource, filename: String?, mimeType: String, useCache: Bool, tempDir: String?)? + open var getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirReceivedInvocations: [(mediaSource: MediaSource, filename: String?, mimeType: String, useCache: Bool, tempDir: String?)] = [] - var getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirUnderlyingReturnValue: MediaFileHandle! - open var getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirReturnValue: MediaFileHandle! { + var getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirUnderlyingReturnValue: MediaFileHandle! + open var getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirReturnValue: MediaFileHandle! { get { if Thread.isMainThread { - return getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirUnderlyingReturnValue + return getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirUnderlyingReturnValue } else { var returnValue: MediaFileHandle? = nil DispatchQueue.main.sync { - returnValue = getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirUnderlyingReturnValue + returnValue = getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirUnderlyingReturnValue } return returnValue! @@ -1207,29 +1207,29 @@ open class ClientSDKMock: MatrixRustSDK.Client { } set { if Thread.isMainThread { - getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirUnderlyingReturnValue = newValue + getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirUnderlyingReturnValue = newValue + getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirUnderlyingReturnValue = newValue } } } } - open var getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirClosure: ((MediaSource, String?, String, Bool, String?) async throws -> MediaFileHandle)? + open var getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirClosure: ((MediaSource, String?, String, Bool, String?) async throws -> MediaFileHandle)? - open override func getMediaFile(mediaSource: MediaSource, body: String?, mimeType: String, useCache: Bool, tempDir: String?) async throws -> MediaFileHandle { - if let error = getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirThrowableError { + open override func getMediaFile(mediaSource: MediaSource, filename: String?, mimeType: String, useCache: Bool, tempDir: String?) async throws -> MediaFileHandle { + if let error = getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirThrowableError { throw error } - getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirCallsCount += 1 - getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirReceivedArguments = (mediaSource: mediaSource, body: body, mimeType: mimeType, useCache: useCache, tempDir: tempDir) + getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirCallsCount += 1 + getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirReceivedArguments = (mediaSource: mediaSource, filename: filename, mimeType: mimeType, useCache: useCache, tempDir: tempDir) DispatchQueue.main.async { - self.getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirReceivedInvocations.append((mediaSource: mediaSource, body: body, mimeType: mimeType, useCache: useCache, tempDir: tempDir)) + self.getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirReceivedInvocations.append((mediaSource: mediaSource, filename: filename, mimeType: mimeType, useCache: useCache, tempDir: tempDir)) } - if let getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirClosure = getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirClosure { - return try await getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirClosure(mediaSource, body, mimeType, useCache, tempDir) + if let getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirClosure = getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirClosure { + return try await getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirClosure(mediaSource, filename, mimeType, useCache, tempDir) } else { - return getMediaFileMediaSourceBodyMimeTypeUseCacheTempDirReturnValue + return getMediaFileMediaSourceFilenameMimeTypeUseCacheTempDirReturnValue } } @@ -2206,6 +2206,81 @@ open class ClientSDKMock: MatrixRustSDK.Client { } } + //MARK: - knock + + open var knockRoomIdOrAliasThrowableError: Error? + var knockRoomIdOrAliasUnderlyingCallsCount = 0 + open var knockRoomIdOrAliasCallsCount: Int { + get { + if Thread.isMainThread { + return knockRoomIdOrAliasUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = knockRoomIdOrAliasUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + knockRoomIdOrAliasUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + knockRoomIdOrAliasUnderlyingCallsCount = newValue + } + } + } + } + open var knockRoomIdOrAliasCalled: Bool { + return knockRoomIdOrAliasCallsCount > 0 + } + open var knockRoomIdOrAliasReceivedRoomIdOrAlias: String? + open var knockRoomIdOrAliasReceivedInvocations: [String] = [] + + var knockRoomIdOrAliasUnderlyingReturnValue: Room! + open var knockRoomIdOrAliasReturnValue: Room! { + get { + if Thread.isMainThread { + return knockRoomIdOrAliasUnderlyingReturnValue + } else { + var returnValue: Room? = nil + DispatchQueue.main.sync { + returnValue = knockRoomIdOrAliasUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + knockRoomIdOrAliasUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + knockRoomIdOrAliasUnderlyingReturnValue = newValue + } + } + } + } + open var knockRoomIdOrAliasClosure: ((String) async throws -> Room)? + + open override func knock(roomIdOrAlias: String) async throws -> Room { + if let error = knockRoomIdOrAliasThrowableError { + throw error + } + knockRoomIdOrAliasCallsCount += 1 + knockRoomIdOrAliasReceivedRoomIdOrAlias = roomIdOrAlias + DispatchQueue.main.async { + self.knockRoomIdOrAliasReceivedInvocations.append(roomIdOrAlias) + } + if let knockRoomIdOrAliasClosure = knockRoomIdOrAliasClosure { + return try await knockRoomIdOrAliasClosure(roomIdOrAlias) + } else { + return knockRoomIdOrAliasReturnValue + } + } + //MARK: - login open var loginUsernamePasswordInitialDeviceNameDeviceIdThrowableError: Error? @@ -6896,164 +6971,6 @@ open class EncryptionSDKMock: MatrixRustSDK.Encryption { await waitForE2eeInitializationTasksClosure?() } } -open class EventShieldsProviderSDKMock: MatrixRustSDK.EventShieldsProvider { - init() { - super.init(noPointer: .init()) - } - - public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { - fatalError("init(unsafeFromRawPointer:) has not been implemented") - } - - fileprivate var pointer: UnsafeMutableRawPointer! - - //MARK: - getShields - - var getShieldsStrictUnderlyingCallsCount = 0 - open var getShieldsStrictCallsCount: Int { - get { - if Thread.isMainThread { - return getShieldsStrictUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = getShieldsStrictUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - getShieldsStrictUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - getShieldsStrictUnderlyingCallsCount = newValue - } - } - } - } - open var getShieldsStrictCalled: Bool { - return getShieldsStrictCallsCount > 0 - } - open var getShieldsStrictReceivedStrict: Bool? - open var getShieldsStrictReceivedInvocations: [Bool] = [] - - var getShieldsStrictUnderlyingReturnValue: ShieldState? - open var getShieldsStrictReturnValue: ShieldState? { - get { - if Thread.isMainThread { - return getShieldsStrictUnderlyingReturnValue - } else { - var returnValue: ShieldState?? = nil - DispatchQueue.main.sync { - returnValue = getShieldsStrictUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - getShieldsStrictUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - getShieldsStrictUnderlyingReturnValue = newValue - } - } - } - } - open var getShieldsStrictClosure: ((Bool) -> ShieldState?)? - - open override func getShields(strict: Bool) -> ShieldState? { - getShieldsStrictCallsCount += 1 - getShieldsStrictReceivedStrict = strict - DispatchQueue.main.async { - self.getShieldsStrictReceivedInvocations.append(strict) - } - if let getShieldsStrictClosure = getShieldsStrictClosure { - return getShieldsStrictClosure(strict) - } else { - return getShieldsStrictReturnValue - } - } -} -open class EventTimelineItemDebugInfoProviderSDKMock: MatrixRustSDK.EventTimelineItemDebugInfoProvider { - init() { - super.init(noPointer: .init()) - } - - public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { - fatalError("init(unsafeFromRawPointer:) has not been implemented") - } - - fileprivate var pointer: UnsafeMutableRawPointer! - - //MARK: - get - - var getUnderlyingCallsCount = 0 - open var getCallsCount: Int { - get { - if Thread.isMainThread { - return getUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = getUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - getUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - getUnderlyingCallsCount = newValue - } - } - } - } - open var getCalled: Bool { - return getCallsCount > 0 - } - - var getUnderlyingReturnValue: EventTimelineItemDebugInfo! - open var getReturnValue: EventTimelineItemDebugInfo! { - get { - if Thread.isMainThread { - return getUnderlyingReturnValue - } else { - var returnValue: EventTimelineItemDebugInfo? = nil - DispatchQueue.main.sync { - returnValue = getUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - getUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - getUnderlyingReturnValue = newValue - } - } - } - } - open var getClosure: (() -> EventTimelineItemDebugInfo)? - - open override func get() -> EventTimelineItemDebugInfo { - getCallsCount += 1 - if let getClosure = getClosure { - return getClosure() - } else { - return getReturnValue - } - } -} open class HomeserverLoginDetailsSDKMock: MatrixRustSDK.HomeserverLoginDetails { init() { super.init(noPointer: .init()) @@ -7624,6 +7541,153 @@ open class InReplyToDetailsSDKMock: MatrixRustSDK.InReplyToDetails { } } } +open class LazyTimelineItemProviderSDKMock: MatrixRustSDK.LazyTimelineItemProvider { + init() { + super.init(noPointer: .init()) + } + + public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + fatalError("init(unsafeFromRawPointer:) has not been implemented") + } + + fileprivate var pointer: UnsafeMutableRawPointer! + + //MARK: - debugInfo + + var debugInfoUnderlyingCallsCount = 0 + open var debugInfoCallsCount: Int { + get { + if Thread.isMainThread { + return debugInfoUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = debugInfoUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + debugInfoUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + debugInfoUnderlyingCallsCount = newValue + } + } + } + } + open var debugInfoCalled: Bool { + return debugInfoCallsCount > 0 + } + + var debugInfoUnderlyingReturnValue: EventTimelineItemDebugInfo! + open var debugInfoReturnValue: EventTimelineItemDebugInfo! { + get { + if Thread.isMainThread { + return debugInfoUnderlyingReturnValue + } else { + var returnValue: EventTimelineItemDebugInfo? = nil + DispatchQueue.main.sync { + returnValue = debugInfoUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + debugInfoUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + debugInfoUnderlyingReturnValue = newValue + } + } + } + } + open var debugInfoClosure: (() -> EventTimelineItemDebugInfo)? + + open override func debugInfo() -> EventTimelineItemDebugInfo { + debugInfoCallsCount += 1 + if let debugInfoClosure = debugInfoClosure { + return debugInfoClosure() + } else { + return debugInfoReturnValue + } + } + + //MARK: - getShields + + var getShieldsStrictUnderlyingCallsCount = 0 + open var getShieldsStrictCallsCount: Int { + get { + if Thread.isMainThread { + return getShieldsStrictUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = getShieldsStrictUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + getShieldsStrictUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + getShieldsStrictUnderlyingCallsCount = newValue + } + } + } + } + open var getShieldsStrictCalled: Bool { + return getShieldsStrictCallsCount > 0 + } + open var getShieldsStrictReceivedStrict: Bool? + open var getShieldsStrictReceivedInvocations: [Bool] = [] + + var getShieldsStrictUnderlyingReturnValue: ShieldState? + open var getShieldsStrictReturnValue: ShieldState? { + get { + if Thread.isMainThread { + return getShieldsStrictUnderlyingReturnValue + } else { + var returnValue: ShieldState?? = nil + DispatchQueue.main.sync { + returnValue = getShieldsStrictUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + getShieldsStrictUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + getShieldsStrictUnderlyingReturnValue = newValue + } + } + } + } + open var getShieldsStrictClosure: ((Bool) -> ShieldState?)? + + open override func getShields(strict: Bool) -> ShieldState? { + getShieldsStrictCallsCount += 1 + getShieldsStrictReceivedStrict = strict + DispatchQueue.main.async { + self.getShieldsStrictReceivedInvocations.append(strict) + } + if let getShieldsStrictClosure = getShieldsStrictClosure { + return getShieldsStrictClosure(strict) + } else { + return getShieldsStrictReturnValue + } + } +} open class MediaFileHandleSDKMock: MatrixRustSDK.MediaFileHandle { init() { super.init(noPointer: .init()) @@ -12488,34 +12552,6 @@ open class RoomSDKMock: MatrixRustSDK.Room { } } - //MARK: - pinUserIdentity - - open var pinUserIdentityUserIdThrowableError: Error? - var pinUserIdentityUserIdUnderlyingCallsCount = 0 - open var pinUserIdentityUserIdCallsCount: Int { - get { - if Thread.isMainThread { - return pinUserIdentityUserIdUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = pinUserIdentityUserIdUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - pinUserIdentityUserIdUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - pinUserIdentityUserIdUnderlyingCallsCount = newValue - } - } - } - } - //MARK: - pinnedEventsTimeline open var pinnedEventsTimelineInternalIdPrefixMaxEventsToLoadMaxConcurrentRequestsThrowableError: Error? diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 32fdf374ab..77d3ee585a 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -845,7 +845,7 @@ class ClientProxy: ClientProxyProtocol { let roomListItem = try roomListService.room(roomId: roomID) switch roomListItem.membership() { - case .invited: + case .invited, .knocked: return try .invited(InvitedRoomProxy(roomListItem: roomListItem, room: roomListItem.invitedRoom())) case .joined: diff --git a/ElementX/Sources/Services/Media/Provider/MediaLoader.swift b/ElementX/Sources/Services/Media/Provider/MediaLoader.swift index fe4e3b2fcc..127dd66d50 100644 --- a/ElementX/Sources/Services/Media/Provider/MediaLoader.swift +++ b/ElementX/Sources/Services/Media/Provider/MediaLoader.swift @@ -35,7 +35,11 @@ actor MediaLoader: MediaLoaderProtocol { } func loadMediaFileForSource(_ source: MediaSourceProxy, filename: String?) async throws -> MediaFileHandleProxy { - let result = try await client.getMediaFile(mediaSource: source.underlyingSource, body: filename, mimeType: source.mimeType ?? "application/octet-stream", useCache: true, tempDir: nil) + let result = try await client.getMediaFile(mediaSource: source.underlyingSource, + filename: filename, + mimeType: source.mimeType ?? "application/octet-stream", + useCache: true, + tempDir: nil) return MediaFileHandleProxy(handle: result) } diff --git a/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift b/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift index b775a8865c..f2b84499a4 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift @@ -162,11 +162,11 @@ class EventTimelineItemProxy { lazy var timestamp = Date(timeIntervalSince1970: TimeInterval(item.timestamp / 1000)) lazy var debugInfo: TimelineItemDebugInfo = { - let debugInfo = item.debugInfoProvider.get() + let debugInfo = item.lazyProvider.debugInfo() return TimelineItemDebugInfo(model: debugInfo.model, originalJSON: debugInfo.originalJson, latestEditJSON: debugInfo.latestEditJson) }() - lazy var shieldState = item.shieldsProvider.getShields(strict: false) + lazy var shieldState = item.lazyProvider.getShields(strict: false) lazy var readReceipts = item.readReceipts } diff --git a/project.yml b/project.yml index e746561dc3..780efa4cb9 100644 --- a/project.yml +++ b/project.yml @@ -60,7 +60,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/element-hq/matrix-rust-components-swift - exactVersion: 1.0.56 + exactVersion: 1.0.57 # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios From 98a5ee5b4853ac8697db65a59d0f5fb886ce3db9 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:48:59 +0100 Subject: [PATCH 054/114] Add a toggle in the developer options to optimise the media uploads. (#3408) --- .../Sources/Application/AppSettings.swift | 5 +- .../RoomFlowCoordinator.swift | 3 +- .../SettingsFlowCoordinator.swift | 1 + .../UserSessionFlowCoordinator.swift | 3 +- .../View/MediaUploadPreviewScreen.swift | 2 +- .../RoomDetailsEditScreenCoordinator.swift | 2 + .../RoomDetailsEditScreenViewModel.swift | 6 +- .../View/RoomDetailsEditScreen.swift | 2 + .../DeveloperOptionsScreenModels.swift | 1 + .../View/DeveloperOptionsScreen.swift | 6 ++ .../UserDetailsEditScreenCoordinator.swift | 2 + .../UserDetailsEditScreenViewModel.swift | 6 +- .../View/UserDetailsEditScreen.swift | 1 + .../StartChatScreenCoordinator.swift | 4 +- .../Media/MediaUploadingPreprocessor.swift | 5 +- .../UITests/UITestsAppCoordinator.swift | 6 +- .../MediaUploadingPreprocessorTests.swift | 87 ++++++++++++++----- .../RoomDetailsEditScreenViewModelTests.swift | 1 + 18 files changed, 109 insertions(+), 34 deletions(-) diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index 1f97ab1f36..fe6d11d335 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -42,9 +42,9 @@ final class AppSettings { // Feature flags case slidingSyncDiscovery + case optimizeMediaUploads case publicSearchEnabled case fuzzyRoomListSearchEnabled - case pinningEnabled case enableOnlySignedDeviceIsolationMode case identityPinningViolationNotificationsEnabled case knockingEnabled @@ -281,6 +281,9 @@ final class AppSettings { @UserPreference(key: UserDefaultsKeys.slidingSyncDiscovery, defaultValue: .native, storageType: .userDefaults(store)) var slidingSyncDiscovery: SlidingSyncDiscovery + @UserPreference(key: UserDefaultsKeys.optimizeMediaUploads, defaultValue: false, storageType: .userDefaults(store)) + var optimizeMediaUploads + @UserPreference(key: UserDefaultsKeys.identityPinningViolationNotificationsEnabled, defaultValue: isDevelopmentBuild, storageType: .userDefaults(store)) var identityPinningViolationNotificationsEnabled diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index 0df9e11bc5..fb48a3cd71 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -810,6 +810,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { let roomDetailsEditParameters = RoomDetailsEditScreenCoordinatorParameters(roomProxy: roomProxy, mediaProvider: userSession.mediaProvider, + mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: appSettings), navigationStackCoordinator: stackCoordinator, userIndicatorController: userIndicatorController, orientationManager: appMediator.windowManager) @@ -895,7 +896,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { let parameters = MediaUploadPreviewScreenCoordinatorParameters(userIndicatorController: userIndicatorController, roomProxy: roomProxy, - mediaUploadingPreprocessor: MediaUploadingPreprocessor(), + mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: appSettings), title: url.lastPathComponent, url: url) diff --git a/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift index e78d5ab59f..7e031a0005 100644 --- a/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift @@ -165,6 +165,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol { let coordinator = UserDetailsEditScreenCoordinator(parameters: .init(orientationManager: parameters.windowManager, clientProxy: parameters.userSession.clientProxy, mediaProvider: parameters.userSession.mediaProvider, + mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: parameters.appSettings), navigationStackCoordinator: navigationStackCoordinator, userIndicatorController: parameters.userIndicatorController)) diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift index a352e2a3f7..ff14107d35 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift @@ -542,7 +542,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { userSession: userSession, userIndicatorController: ServiceLocator.shared.userIndicatorController, navigationStackCoordinator: startChatNavigationStackCoordinator, - userDiscoveryService: userDiscoveryService) + userDiscoveryService: userDiscoveryService, + mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: appSettings)) let coordinator = StartChatScreenCoordinator(parameters: parameters) coordinator.actions.sink { [weak self] action in diff --git a/ElementX/Sources/Screens/MediaUploadPreviewScreen/View/MediaUploadPreviewScreen.swift b/ElementX/Sources/Screens/MediaUploadPreviewScreen/View/MediaUploadPreviewScreen.swift index c902a1ba48..30db52e56c 100644 --- a/ElementX/Sources/Screens/MediaUploadPreviewScreen/View/MediaUploadPreviewScreen.swift +++ b/ElementX/Sources/Screens/MediaUploadPreviewScreen/View/MediaUploadPreviewScreen.swift @@ -116,7 +116,7 @@ private class PreviewItem: NSObject, QLPreviewItem { struct MediaUploadPreviewScreen_Previews: PreviewProvider, TestablePreview { static let viewModel = MediaUploadPreviewScreenViewModel(userIndicatorController: UserIndicatorControllerMock.default, roomProxy: JoinedRoomProxyMock(), - mediaUploadingPreprocessor: MediaUploadingPreprocessor(), + mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings), title: "some random file name", url: URL.picturesDirectory) static var previews: some View { diff --git a/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenCoordinator.swift b/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenCoordinator.swift index 0bec89382e..9d4dffcf52 100644 --- a/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenCoordinator.swift +++ b/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenCoordinator.swift @@ -11,6 +11,7 @@ import SwiftUI struct RoomDetailsEditScreenCoordinatorParameters { let roomProxy: JoinedRoomProxyProtocol let mediaProvider: MediaProviderProtocol + let mediaUploadingPreprocessor: MediaUploadingPreprocessor weak var navigationStackCoordinator: NavigationStackCoordinator? let userIndicatorController: UserIndicatorControllerProtocol let orientationManager: OrientationManagerProtocol @@ -35,6 +36,7 @@ final class RoomDetailsEditScreenCoordinator: CoordinatorProtocol { viewModel = RoomDetailsEditScreenViewModel(roomProxy: parameters.roomProxy, mediaProvider: parameters.mediaProvider, + mediaUploadingPreprocessor: parameters.mediaUploadingPreprocessor, userIndicatorController: parameters.userIndicatorController) } diff --git a/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenViewModel.swift b/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenViewModel.swift index 26fa1f0a26..cc16d6a709 100644 --- a/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenViewModel.swift @@ -14,7 +14,7 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe private let actionsSubject: PassthroughSubject = .init() private let roomProxy: JoinedRoomProxyProtocol private let userIndicatorController: UserIndicatorControllerProtocol - private let mediaPreprocessor: MediaUploadingPreprocessor = .init() + private let mediaUploadingPreprocessor: MediaUploadingPreprocessor var actions: AnyPublisher { actionsSubject.eraseToAnyPublisher() @@ -22,8 +22,10 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe init(roomProxy: JoinedRoomProxyProtocol, mediaProvider: MediaProviderProtocol, + mediaUploadingPreprocessor: MediaUploadingPreprocessor, userIndicatorController: UserIndicatorControllerProtocol) { self.roomProxy = roomProxy + self.mediaUploadingPreprocessor = mediaUploadingPreprocessor self.userIndicatorController = userIndicatorController let roomAvatar = roomProxy.avatarURL @@ -76,7 +78,7 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe title: L10n.commonLoading, persistent: true)) - let mediaResult = await mediaPreprocessor.processMedia(at: url) + let mediaResult = await mediaUploadingPreprocessor.processMedia(at: url) switch mediaResult { case .success(.image): diff --git a/ElementX/Sources/Screens/RoomDetailsEditScreen/View/RoomDetailsEditScreen.swift b/ElementX/Sources/Screens/RoomDetailsEditScreen/View/RoomDetailsEditScreen.swift index 3f4cd5fd35..9705d0ce3b 100644 --- a/ElementX/Sources/Screens/RoomDetailsEditScreen/View/RoomDetailsEditScreen.swift +++ b/ElementX/Sources/Screens/RoomDetailsEditScreen/View/RoomDetailsEditScreen.swift @@ -154,6 +154,7 @@ struct RoomDetailsEditScreen_Previews: PreviewProvider, TestablePreview { return RoomDetailsEditScreenViewModel(roomProxy: roomProxy, mediaProvider: MediaProviderMock(configuration: .init()), + mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings), userIndicatorController: UserIndicatorControllerMock.default) }() @@ -164,6 +165,7 @@ struct RoomDetailsEditScreen_Previews: PreviewProvider, TestablePreview { return RoomDetailsEditScreenViewModel(roomProxy: roomProxy, mediaProvider: MediaProviderMock(configuration: .init()), + mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings), userIndicatorController: UserIndicatorControllerMock.default) }() diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift index 7e186b3a05..1d282d462d 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift @@ -46,6 +46,7 @@ protocol DeveloperOptionsProtocol: AnyObject { var hideUnreadMessagesBadge: Bool { get set } var fuzzyRoomListSearchEnabled: Bool { get set } var hideTimelineMedia: Bool { get set } + var optimizeMediaUploads: Bool { get set } var enableOnlySignedDeviceIsolationMode: Bool { get set } var elementCallBaseURLOverride: URL? { get set } var identityPinningViolationNotificationsEnabled: Bool { get set } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift index 42a3000faa..22f7044896 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -62,6 +62,12 @@ struct DeveloperOptionsScreen: View { } } + Section("Media") { + Toggle(isOn: $context.optimizeMediaUploads) { + Text("Optimise for upload") + } + } + Section { Toggle(isOn: $context.enableOnlySignedDeviceIsolationMode) { Text("Exclude insecure devices when sending/receiving messages") diff --git a/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenCoordinator.swift b/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenCoordinator.swift index b4eb6e3361..bcb06ec35d 100644 --- a/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenCoordinator.swift @@ -12,6 +12,7 @@ struct UserDetailsEditScreenCoordinatorParameters { let orientationManager: OrientationManagerProtocol let clientProxy: ClientProxyProtocol let mediaProvider: MediaProviderProtocol + let mediaUploadingPreprocessor: MediaUploadingPreprocessor weak var navigationStackCoordinator: NavigationStackCoordinator? let userIndicatorController: UserIndicatorControllerProtocol } @@ -26,6 +27,7 @@ final class UserDetailsEditScreenCoordinator: CoordinatorProtocol { viewModel = UserDetailsEditScreenViewModel(clientProxy: parameters.clientProxy, mediaProvider: parameters.mediaProvider, + mediaUploadingPreprocessor: parameters.mediaUploadingPreprocessor, userIndicatorController: parameters.userIndicatorController) } diff --git a/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenViewModel.swift b/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenViewModel.swift index f83b911fda..0b03ed0d15 100644 --- a/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenViewModel.swift +++ b/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenViewModel.swift @@ -14,7 +14,7 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe private let actionsSubject: PassthroughSubject = .init() private let clientProxy: ClientProxyProtocol private let userIndicatorController: UserIndicatorControllerProtocol - private let mediaPreprocessor: MediaUploadingPreprocessor = .init() + private let mediaUploadingPreprocessor: MediaUploadingPreprocessor var actions: AnyPublisher { actionsSubject.eraseToAnyPublisher() @@ -22,8 +22,10 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe init(clientProxy: ClientProxyProtocol, mediaProvider: MediaProviderProtocol, + mediaUploadingPreprocessor: MediaUploadingPreprocessor, userIndicatorController: UserIndicatorControllerProtocol) { self.clientProxy = clientProxy + self.mediaUploadingPreprocessor = mediaUploadingPreprocessor self.userIndicatorController = userIndicatorController super.init(initialViewState: UserDetailsEditScreenViewState(userID: clientProxy.userID, @@ -88,7 +90,7 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe title: L10n.commonLoading, persistent: true)) - let mediaResult = await mediaPreprocessor.processMedia(at: url) + let mediaResult = await mediaUploadingPreprocessor.processMedia(at: url) switch mediaResult { case .success(.image): diff --git a/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/View/UserDetailsEditScreen.swift b/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/View/UserDetailsEditScreen.swift index 9049acea55..953f3f96d3 100644 --- a/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/View/UserDetailsEditScreen.swift +++ b/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/View/UserDetailsEditScreen.swift @@ -117,6 +117,7 @@ struct UserDetailsEditScreen: View { struct UserDetailsEditScreen_Previews: PreviewProvider, TestablePreview { static let viewModel = UserDetailsEditScreenViewModel(clientProxy: ClientProxyMock(.init(userID: "@stefan:matrix.org")), mediaProvider: MediaProviderMock(configuration: .init()), + mediaUploadingPreprocessor: .init(appSettings: ServiceLocator.shared.settings), userIndicatorController: UserIndicatorControllerMock.default) static var previews: some View { diff --git a/ElementX/Sources/Screens/StartChatScreen/StartChatScreenCoordinator.swift b/ElementX/Sources/Screens/StartChatScreen/StartChatScreenCoordinator.swift index 56791a1a4a..2bc682ef4e 100644 --- a/ElementX/Sources/Screens/StartChatScreen/StartChatScreenCoordinator.swift +++ b/ElementX/Sources/Screens/StartChatScreen/StartChatScreenCoordinator.swift @@ -14,6 +14,7 @@ struct StartChatScreenCoordinatorParameters { let userIndicatorController: UserIndicatorControllerProtocol weak var navigationStackCoordinator: NavigationStackCoordinator? let userDiscoveryService: UserDiscoveryServiceProtocol + let mediaUploadingPreprocessor: MediaUploadingPreprocessor } enum StartChatScreenCoordinatorAction { @@ -134,7 +135,6 @@ final class StartChatScreenCoordinator: CoordinatorProtocol { // MARK: - Private - let mediaUploadingPreprocessor = MediaUploadingPreprocessor() private func displayMediaPickerWithSource(_ source: MediaPickerScreenSource) { let stackCoordinator = NavigationStackCoordinator() @@ -159,7 +159,7 @@ final class StartChatScreenCoordinator: CoordinatorProtocol { Task { [weak self] in guard let self else { return } do { - let media = try await mediaUploadingPreprocessor.processMedia(at: url).get() + let media = try await parameters.mediaUploadingPreprocessor.processMedia(at: url).get() var parameters = createRoomParameters.value parameters.avatarImageMedia = media createRoomParameters.send(parameters) diff --git a/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift b/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift index cec36f19c7..ef88c3d797 100644 --- a/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift +++ b/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift @@ -82,6 +82,8 @@ private struct VideoProcessingInfo { } struct MediaUploadingPreprocessor { + let appSettings: AppSettings + enum Constants { static let maximumThumbnailSize = CGSize(width: 800, height: 600) static let thumbnailCompressionQuality = 0.8 @@ -368,8 +370,9 @@ struct MediaUploadingPreprocessor { /// - Returns: the URL for the resulting video and its media info as a `VideoProcessingResult` private func convertVideoToMP4(_ url: URL, targetFileSize: UInt = 0) async -> Result { let asset = AVURLAsset(url: url) + let presetName = appSettings.optimizeMediaUploads ? AVAssetExportPreset640x480 : AVAssetExportPreset1920x1080 - guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset1920x1080) else { + guard let exportSession = AVAssetExportSession(asset: asset, presetName: presetName) else { return .failure(.failedConvertingVideo) } diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index 1c125258bc..fb12887859 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -581,7 +581,8 @@ class MockScreen: Identifiable { userSession: userSession, userIndicatorController: UserIndicatorControllerMock(), navigationStackCoordinator: navigationStackCoordinator, - userDiscoveryService: userDiscoveryMock) + userDiscoveryService: userDiscoveryMock, + mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings)) let coordinator = StartChatScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator @@ -595,7 +596,8 @@ class MockScreen: Identifiable { userSession: userSession, userIndicatorController: UserIndicatorControllerMock(), navigationStackCoordinator: navigationStackCoordinator, - userDiscoveryService: userDiscoveryMock)) + userDiscoveryService: userDiscoveryMock, + mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings))) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator case .createRoom: diff --git a/UnitTests/Sources/MediaUploadingPreprocessorTests.swift b/UnitTests/Sources/MediaUploadingPreprocessorTests.swift index e33bc64b7a..8f4e7c8443 100644 --- a/UnitTests/Sources/MediaUploadingPreprocessorTests.swift +++ b/UnitTests/Sources/MediaUploadingPreprocessorTests.swift @@ -10,7 +10,19 @@ import XCTest @testable import ElementX final class MediaUploadingPreprocessorTests: XCTestCase { - let mediaUploadingPreprocessor = MediaUploadingPreprocessor() + var appSettings: AppSettings! + var mediaUploadingPreprocessor: MediaUploadingPreprocessor! + + override func setUp() { + AppSettings.resetAllSettings() + appSettings = AppSettings() + ServiceLocator.shared.register(appSettings: appSettings) + mediaUploadingPreprocessor = MediaUploadingPreprocessor(appSettings: appSettings) + } + + override func tearDown() { + AppSettings.resetAllSettings() + } func testAudioFileProcessing() async { guard let url = Bundle(for: Self.self).url(forResource: "test_audio.mp3", withExtension: nil) else { @@ -70,6 +82,23 @@ final class MediaUploadingPreprocessorTests: XCTestCase { XCTAssertEqual(videoInfo.thumbnailInfo?.size ?? 0, 34206, accuracy: 100) XCTAssertEqual(videoInfo.thumbnailInfo?.width, 800) XCTAssertEqual(videoInfo.thumbnailInfo?.height, 450) + + // Repeat with optimised media setting + appSettings.optimizeMediaUploads = true + + guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url), + case let .video(_, _, optimizedVideoInfo) = optimizedResult else { + XCTFail("Failed processing asset") + return + } + + // Check optimised video info + XCTAssertEqual(optimizedVideoInfo.mimetype, "video/mp4") + XCTAssertEqual(optimizedVideoInfo.blurhash, "K32PJbx^I7jYaebHMvV?o$") + XCTAssertEqual(optimizedVideoInfo.size ?? 0, 4_090_898, accuracy: 100) // Note: This is slightly stupid because it is larger now 🤦‍♂️ + XCTAssertEqual(optimizedVideoInfo.width, 640) + XCTAssertEqual(optimizedVideoInfo.height, 360) + XCTAssertEqual(optimizedVideoInfo.duration ?? 0, 30, accuracy: 100) } func testPortraitMp4VideoProcessing() async { @@ -110,6 +139,23 @@ final class MediaUploadingPreprocessorTests: XCTestCase { XCTAssertEqual(videoInfo.thumbnailInfo?.size ?? 0, 83220, accuracy: 100) XCTAssertEqual(videoInfo.thumbnailInfo?.width, 337) XCTAssertEqual(videoInfo.thumbnailInfo?.height, 600) + + // Repeat with optimised media setting + appSettings.optimizeMediaUploads = true + + guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url), + case let .video(_, _, optimizedVideoInfo) = optimizedResult else { + XCTFail("Failed processing asset") + return + } + + // Check optimised video info + XCTAssertEqual(optimizedVideoInfo.mimetype, "video/mp4") + XCTAssertEqual(optimizedVideoInfo.blurhash, "K7BDNJD*0L%#sl_2~C9ZE1") + XCTAssertEqual(optimizedVideoInfo.size ?? 0, 6_520_897, accuracy: 100) + XCTAssertEqual(optimizedVideoInfo.width, 360) + XCTAssertEqual(optimizedVideoInfo.height, 640) + XCTAssertEqual(optimizedVideoInfo.duration ?? 0, 30, accuracy: 100) } func testLandscapeImageProcessing() async { @@ -185,28 +231,10 @@ final class MediaUploadingPreprocessorTests: XCTestCase { XCTAssertEqual(originalImage.size, convertedImage.size) // Check that the GPS data has been stripped - guard let imageSource = CGImageSourceCreateWithData(originalImageData as NSData, nil) else { - XCTFail("Invalid test asset") - return - } - - guard let originalMetadata: NSDictionary = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) else { - XCTFail("Test asset is expected to contain metadata") - return - } - + let originalMetadata = metadata(from: originalImageData) XCTAssertNotNil(originalMetadata.value(forKeyPath: "\(kCGImagePropertyGPSDictionary)")) - guard let convertedImageSource = CGImageSourceCreateWithData(convertedImageData as NSData, nil) else { - XCTFail("Invalid converted asset") - return - } - - guard let convertedMetadata: NSDictionary = CGImageSourceCopyPropertiesAtIndex(convertedImageSource, 0, nil) else { - XCTFail("Test asset is expected to contain metadata") - return - } - + let convertedMetadata = metadata(from: convertedImageData) XCTAssertNil(convertedMetadata.value(forKeyPath: "\(kCGImagePropertyGPSDictionary)")) // Check that the thumbnail is generated correctly @@ -223,5 +251,22 @@ final class MediaUploadingPreprocessorTests: XCTestCase { XCTAssert(thumbnail.size.width <= MediaUploadingPreprocessor.Constants.maximumThumbnailSize.height) XCTAssert(thumbnail.size.height <= MediaUploadingPreprocessor.Constants.maximumThumbnailSize.width) } + + let thumbnailMetadata = metadata(from: thumbnailData) + XCTAssertNil(thumbnailMetadata.value(forKeyPath: "\(kCGImagePropertyGPSDictionary)")) + } + + private func metadata(from imageData: Data) -> NSDictionary { + guard let imageSource = CGImageSourceCreateWithData(imageData as NSData, nil) else { + XCTFail("Invalid asset") + return [:] + } + + guard let convertedMetadata: NSDictionary = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) else { + XCTFail("Test asset is expected to contain metadata") + return [:] + } + + return convertedMetadata } } diff --git a/UnitTests/Sources/RoomDetailsEditScreenViewModelTests.swift b/UnitTests/Sources/RoomDetailsEditScreenViewModelTests.swift index a21ec8c104..2088df6e3e 100644 --- a/UnitTests/Sources/RoomDetailsEditScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomDetailsEditScreenViewModelTests.swift @@ -112,6 +112,7 @@ class RoomDetailsEditScreenViewModelTests: XCTestCase { userIndicatorController = UserIndicatorControllerMock.default viewModel = .init(roomProxy: JoinedRoomProxyMock(roomProxyConfiguration), mediaProvider: MediaProviderMock(configuration: .init()), + mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings), userIndicatorController: userIndicatorController) } } From aad423dacc3d12fbb1298f004b9605bed262c20b Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Tue, 15 Oct 2024 08:44:06 +0300 Subject: [PATCH 055/114] Fix integration tests after `clearAndTypeText` signature change. --- IntegrationTests/Sources/Common.swift | 6 +++--- IntegrationTests/Sources/UserFlowTests.swift | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/IntegrationTests/Sources/Common.swift b/IntegrationTests/Sources/Common.swift index e79483605a..b540a3b11d 100644 --- a/IntegrationTests/Sources/Common.swift +++ b/IntegrationTests/Sources/Common.swift @@ -21,7 +21,7 @@ extension XCUIApplication { let homeserverTextField = textFields[A11yIdentifiers.changeServerScreen.server] XCTAssertTrue(homeserverTextField.waitForExistence(timeout: 10.0)) - homeserverTextField.clearAndTypeText(homeserver) + homeserverTextField.clearAndTypeText(homeserver, app: self) let confirmButton = buttons[A11yIdentifiers.changeServerScreen.continue] XCTAssertTrue(confirmButton.waitForExistence(timeout: 10.0)) @@ -39,12 +39,12 @@ extension XCUIApplication { let usernameTextField = textFields[A11yIdentifiers.loginScreen.emailUsername] XCTAssertTrue(usernameTextField.waitForExistence(timeout: 10.0)) - usernameTextField.clearAndTypeText(username) + usernameTextField.clearAndTypeText(username, app: self) let passwordTextField = secureTextFields[A11yIdentifiers.loginScreen.password] XCTAssertTrue(passwordTextField.waitForExistence(timeout: 10.0)) - passwordTextField.clearAndTypeText(password) + passwordTextField.clearAndTypeText(password, app: self) let nextButton = buttons[A11yIdentifiers.loginScreen.continue] XCTAssertTrue(nextButton.waitForExistence(timeout: 10.0)) diff --git a/IntegrationTests/Sources/UserFlowTests.swift b/IntegrationTests/Sources/UserFlowTests.swift index 46ae747e8d..04edd6bcc5 100644 --- a/IntegrationTests/Sources/UserFlowTests.swift +++ b/IntegrationTests/Sources/UserFlowTests.swift @@ -32,7 +32,7 @@ class UserFlowTests: XCTestCase { private func checkRoomFlows() { // Search for the special test room let searchField = app.searchFields.firstMatch - searchField.clearAndTypeText(Self.integrationTestsRoomName) + searchField.clearAndTypeText(Self.integrationTestsRoomName, app: app) // And open it let firstRoom = app.buttons.matching(NSPredicate(format: "identifier CONTAINS %@", Self.integrationTestsRoomName)).firstMatch @@ -63,7 +63,7 @@ class UserFlowTests: XCTestCase { private func sendMessages() { var composerTextField = app.textViews[A11yIdentifiers.roomScreen.messageComposer].firstMatch XCTAssertTrue(composerTextField.waitForExistence(timeout: 10.0)) - composerTextField.clearAndTypeText(Self.integrationTestsMessage) + composerTextField.clearAndTypeText(Self.integrationTestsMessage, app: app) var sendButton = app.buttons[A11yIdentifiers.roomScreen.sendButton].firstMatch XCTAssertTrue(sendButton.waitForExistence(timeout: 10.0)) @@ -77,7 +77,7 @@ class UserFlowTests: XCTestCase { composerTextField = app.textViews[A11yIdentifiers.roomScreen.messageComposer].firstMatch XCTAssertTrue(composerTextField.waitForExistence(timeout: 10.0)) - composerTextField.clearAndTypeText(Self.integrationTestsMessage) + composerTextField.clearAndTypeText(Self.integrationTestsMessage, app: app) sendButton = app.buttons[A11yIdentifiers.roomScreen.sendButton].firstMatch XCTAssertTrue(sendButton.waitForExistence(timeout: 10.0)) From 236d1425834f92ab8ed0032c716beaa52d6e4b0d Mon Sep 17 00:00:00 2001 From: Element CI Date: Tue, 15 Oct 2024 04:21:58 -0700 Subject: [PATCH 056/114] Prepare next release --- CHANGES.md | 17 +++++++++++++++++ ElementX.xcodeproj/project.pbxproj | 4 ++-- project.yml | 2 +- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 879a55503a..7dbb999810 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,20 @@ +## Changes in 1.9.1 (2024-10-15) + +### What's Changed + +🐛 Bugfixes +* Fix a bug opening images with a valid filename but a mimetype of `image/*` (sent by EXA). by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3407 + +🗣 Translations +* Translations update by @RiotRobot in https://github.com/element-hq/element-x-ios/pull/3406 + +🚧 In development 🚧 +* Create Room with knock rule by @Velin92 in https://github.com/element-hq/element-x-ios/pull/3397 +* Allow video uploads to be optimised to reduce bandwidth. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3408 + + +**Full Changelog**: https://github.com/element-hq/element-x-ios/compare/1.9.0...1.9.1 + ## Changes in 1.9.0 (2024-10-10) ### What's Changed diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index e2195795a7..1af189e9c8 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7433,7 +7433,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.9.1; + MARKETING_VERSION = 1.9.2; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCTION_APP_NAME = Element; @@ -7510,7 +7510,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.9.1; + MARKETING_VERSION = 1.9.2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; diff --git a/project.yml b/project.yml index 780efa4cb9..1064f024d8 100644 --- a/project.yml +++ b/project.yml @@ -41,7 +41,7 @@ settings: APP_GROUP_IDENTIFIER: group.$(BASE_APP_GROUP_IDENTIFIER) APP_NAME: ElementX KEYCHAIN_ACCESS_GROUP_IDENTIFIER: "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)" - MARKETING_VERSION: 1.9.1 + MARKETING_VERSION: 1.9.2 CURRENT_PROJECT_VERSION: 1 SUPPORTS_MACCATALYST: false From 718774dc365e47c5152217f159f81f7fafb9d94d Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Wed, 16 Oct 2024 08:54:34 +0100 Subject: [PATCH 057/114] Fix some warnings. (#3416) --- .../AuthenticationFlowCoordinator.swift | 53 +++++++++---------- .../Sources/Other/Extensions/Dictionary.swift | 2 +- .../CallScreen/CallScreenViewModel.swift | 14 +++-- .../CreateRoom/CreateRoomViewModel.swift | 2 +- .../Services/BugReport/BugReportService.swift | 4 +- .../Sources/Services/Client/ClientProxy.swift | 1 + .../Services/Client/ClientProxyProtocol.swift | 1 + .../ElementCall/ElementCallService.swift | 2 - .../Services/Timeline/TimelineItemProxy.swift | 2 +- .../Sources/UITests/UITestsSignalling.swift | 10 ++-- 10 files changed, 48 insertions(+), 43 deletions(-) diff --git a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift index 9425920ede..5edc7967dd 100644 --- a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift @@ -137,33 +137,6 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { bugReportFlowCoordinator?.start() } - // TODO: Move this method after showServerConfirmationScreen - private func showServerSelectionScreen(authenticationFlow: AuthenticationFlow) { - let navigationCoordinator = NavigationStackCoordinator() - - let parameters = ServerSelectionScreenCoordinatorParameters(authenticationService: authenticationService, - authenticationFlow: authenticationFlow, - slidingSyncLearnMoreURL: appSettings.slidingSyncLearnMoreURL, - userIndicatorController: userIndicatorController) - let coordinator = ServerSelectionScreenCoordinator(parameters: parameters) - - coordinator.actions - .sink { [weak self] action in - guard let self else { return } - - switch action { - case .updated: - navigationStackCoordinator.setSheetCoordinator(nil) - case .dismiss: - navigationStackCoordinator.setSheetCoordinator(nil) - } - } - .store(in: &cancellables) - - navigationCoordinator.setRootCoordinator(coordinator) - navigationStackCoordinator.setSheetCoordinator(navigationCoordinator) - } - private func showServerConfirmationScreen(authenticationFlow: AuthenticationFlow) { // Reset the service back to the default homeserver before continuing. This ensures // we check that registration is supported if it was previously configured for login. @@ -196,6 +169,32 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { navigationStackCoordinator.push(coordinator) } + private func showServerSelectionScreen(authenticationFlow: AuthenticationFlow) { + let navigationCoordinator = NavigationStackCoordinator() + + let parameters = ServerSelectionScreenCoordinatorParameters(authenticationService: authenticationService, + authenticationFlow: authenticationFlow, + slidingSyncLearnMoreURL: appSettings.slidingSyncLearnMoreURL, + userIndicatorController: userIndicatorController) + let coordinator = ServerSelectionScreenCoordinator(parameters: parameters) + + coordinator.actions + .sink { [weak self] action in + guard let self else { return } + + switch action { + case .updated: + navigationStackCoordinator.setSheetCoordinator(nil) + case .dismiss: + navigationStackCoordinator.setSheetCoordinator(nil) + } + } + .store(in: &cancellables) + + navigationCoordinator.setRootCoordinator(coordinator) + navigationStackCoordinator.setSheetCoordinator(navigationCoordinator) + } + private func showWebRegistration() { let parameters = WebRegistrationScreenCoordinatorParameters(authenticationService: authenticationService, userIndicatorController: userIndicatorController) diff --git a/ElementX/Sources/Other/Extensions/Dictionary.swift b/ElementX/Sources/Other/Extensions/Dictionary.swift index 2fe7ddc8f3..14e064dcdd 100644 --- a/ElementX/Sources/Other/Extensions/Dictionary.swift +++ b/ElementX/Sources/Other/Extensions/Dictionary.swift @@ -13,7 +13,7 @@ extension Dictionary { options: [.fragmentsAllowed, .sortedKeys]) else { return nil } - return String(decoding: data, as: UTF8.self) + return String(data: data, encoding: .utf8) } /// Returns a dictionary containing the original values keyed by the results of mapping the given closure over its keys. diff --git a/ElementX/Sources/Screens/CallScreen/CallScreenViewModel.swift b/ElementX/Sources/Screens/CallScreen/CallScreenViewModel.swift index abbc817bb6..1e9e0315aa 100644 --- a/ElementX/Sources/Screens/CallScreen/CallScreenViewModel.swift +++ b/ElementX/Sources/Screens/CallScreen/CallScreenViewModel.swift @@ -213,14 +213,20 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol } private func postMessageToWidget(_ message: ElementCallWidgetMessage) async { + let data: Data do { - let data = try JSONEncoder().encode(message) - let json = String(decoding: data, as: UTF8.self) - - await postJSONToWidget(json) + data = try JSONEncoder().encode(message) } catch { MXLog.error("Failed encoding widget message with error: \(error)") + return } + + guard let json = String(data: data, encoding: .utf8) else { + MXLog.error("Invalid data for widget message") + return + } + + await postJSONToWidget(json) } private func postJSONToWidget(_ json: String) async { diff --git a/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift b/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift index 39fde2fcda..da70c7afb2 100644 --- a/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift +++ b/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift @@ -93,7 +93,7 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol old.roomName == new.roomName && old.roomTopic == new.roomTopic && old.isRoomPrivate == new.isRoomPrivate } .sink { [weak self] bindings in - guard let self = self else { return } + guard let self else { return } updateParameters(bindings: bindings) actionsSubject.send(.updateDetails(createRoomParameters)) } diff --git a/ElementX/Sources/Services/BugReport/BugReportService.swift b/ElementX/Sources/Services/BugReport/BugReportService.swift index f263f653fb..75037c4b30 100644 --- a/ElementX/Sources/Services/BugReport/BugReportService.swift +++ b/ElementX/Sources/Services/BugReport/BugReportService.swift @@ -117,14 +117,14 @@ class BugReportService: NSObject, BugReportServiceProtocol { let (data, response) = try await session.dataWithRetry(for: request, delegate: self) guard let httpResponse = response as? HTTPURLResponse else { - let errorDescription = String(decoding: data, as: UTF8.self).trimmingCharacters(in: .whitespacesAndNewlines) + let errorDescription = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "Unknown error" MXLog.error("Failed to submit bug report: \(errorDescription)") MXLog.error("Response: \(response)") return .failure(.serverError(response, errorDescription)) } guard httpResponse.statusCode == 200 else { - let errorDescription = String(decoding: data, as: UTF8.self).trimmingCharacters(in: .whitespacesAndNewlines) + let errorDescription = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "Unknown error" MXLog.error("Failed to submit bug report: \(errorDescription) (\(httpResponse.statusCode))") MXLog.error("Response: \(httpResponse)") return .failure(.httpError(httpResponse, errorDescription)) diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 77d3ee585a..72a647283a 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -365,6 +365,7 @@ class ClientProxy: ClientProxyProtocol { } } + // swiftlint:disable:next function_parameter_count func createRoom(name: String, topic: String?, isRoomPrivate: Bool, isKnockingOnly: Bool, userIDs: [String], avatarURL: URL?) async -> Result { do { // TODO: Revisit once the SDK supports the knocking API diff --git a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift index 0726ba83e6..e8160f2412 100644 --- a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift +++ b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift @@ -138,6 +138,7 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol { func createDirectRoom(with userID: String, expectedRoomName: String?) async -> Result + // swiftlint:disable:next function_parameter_count func createRoom(name: String, topic: String?, isRoomPrivate: Bool, isKnockingOnly: Bool, userIDs: [String], avatarURL: URL?) async -> Result func joinRoom(_ roomID: String, via: [String]) async -> Result diff --git a/ElementX/Sources/Services/ElementCall/ElementCallService.swift b/ElementX/Sources/Services/ElementCall/ElementCallService.swift index 049f4c49bf..f08a4e712a 100644 --- a/ElementX/Sources/Services/ElementCall/ElementCallService.swift +++ b/ElementX/Sources/Services/ElementCall/ElementCallService.swift @@ -299,8 +299,6 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe switch action { case .roomInfoUpdate: return (roomProxy.hasOngoingCall, roomProxy.activeRoomCallParticipants) - default: - return nil } } .removeDuplicates { $0 == $1 } diff --git a/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift b/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift index f2b84499a4..baced33cb3 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift @@ -208,7 +208,7 @@ struct TimelineItemDebugInfo: Identifiable, CustomStringConvertible { return nil } - return String(decoding: jsonData, as: UTF8.self) + return String(data: jsonData, encoding: .utf8) } } diff --git a/ElementX/Sources/UITests/UITestsSignalling.swift b/ElementX/Sources/UITests/UITestsSignalling.swift index 84f054e2ef..ece07bfb21 100644 --- a/ElementX/Sources/UITests/UITestsSignalling.swift +++ b/ElementX/Sources/UITests/UITestsSignalling.swift @@ -137,10 +137,11 @@ enum UITestsSignalling { let encoder = JSONEncoder() encoder.outputFormatting = .sortedKeys - guard let data = try? encoder.encode(self) else { + guard let data = try? encoder.encode(self), + let rawMessage = String(data: data, encoding: .utf8) else { return "unknown" } - return String(decoding: data, as: UTF8.self) + return rawMessage } init?(rawValue: String) { @@ -165,9 +166,8 @@ enum UITestsSignalling { /// Processes string data from the file and publishes its signal. private func processFileData(_ data: Data) { - let rawMessage = String(decoding: data, as: UTF8.self) - - guard let message = Message(rawValue: rawMessage), + guard let rawMessage = String(data: data, encoding: .utf8), + let message = Message(rawValue: rawMessage), message.mode != mode // Filter out messages sent by this client. else { return } From 3d797ccabbfd382efaa11b731891c0a5eb591a8f Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Wed, 16 Oct 2024 10:40:29 +0100 Subject: [PATCH 058/114] Only subscribe to identity updates if the room is encrypted. (#3414) --- ElementX/Sources/Services/Room/JoinedRoomProxy.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ElementX/Sources/Services/Room/JoinedRoomProxy.swift b/ElementX/Sources/Services/Room/JoinedRoomProxy.swift index cdcd4fc19c..13d3f75921 100644 --- a/ElementX/Sources/Services/Room/JoinedRoomProxy.swift +++ b/ElementX/Sources/Services/Room/JoinedRoomProxy.swift @@ -200,7 +200,9 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol { subscribeToRoomInfoUpdates() - subscribeToIdentityStatusChanges() + if isEncrypted { + subscribeToIdentityStatusChanges() + } subscribeToTypingNotifications() } From 35d49c4d85ac0948596c421fb58c9efe7d5c54d7 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 16 Oct 2024 14:07:54 +0300 Subject: [PATCH 059/114] Refactor the`TimelineItemIdentifier` handling; stop relying on optional `EventOrTransactionId`s and be explicit when setting composer modes from the draft service. --- ElementX.xcodeproj/project.pbxproj | 4 ++ .../Sources/Application/AppCoordinator.swift | 1 + .../Mocks/Generated/GeneratedMocks.swift | 62 ++++++++--------- ElementX/Sources/Mocks/PollMock.swift | 2 +- .../View/MessageForwardingScreen.swift | 2 +- ...ResolveVerifiedUserSendFailureScreen.swift | 4 +- .../ComposerToolbarModels.swift | 14 ++-- .../ComposerToolbarViewModel.swift | 20 ++---- .../View/ComposerToolbar.swift | 4 +- .../View/MessageComposer.swift | 8 +-- .../Timeline/TimelineInteractionHandler.swift | 9 ++- .../Screens/Timeline/TimelineViewModel.swift | 10 +-- .../View/Style/SwipeToReplyView.swift | 2 +- .../Style/TimelineItemBubbledStylerView.swift | 22 +++---- .../Style/TimelineItemSendInfoLabel.swift | 10 +-- .../Timeline/View/Style/TimelineStyler.swift | 18 ++--- .../Supplementary/TimelineReactionsView.swift | 8 +-- .../TimelineReadReceiptsView.swift | 2 +- .../AudioRoomTimelineView.swift | 2 +- .../CallInviteRoomTimelineView.swift | 2 +- .../CallNotificationRoomTimelineView.swift | 2 +- .../CollapsibleRoomTimelineView.swift | 4 +- .../EmoteRoomTimelineView.swift | 2 +- .../EncryptedRoomTimelineView.swift | 4 +- .../FileRoomTimelineView.swift | 6 +- .../ImageRoomTimelineView.swift | 6 +- .../LocationRoomTimelineView.swift | 6 +- .../NoticeRoomTimelineView.swift | 2 +- .../ReadMarkerRoomTimelineView.swift | 10 +-- .../RedactedRoomTimelineView.swift | 2 +- .../SeparatorRoomTimelineView.swift | 3 +- .../StateRoomTimelineView.swift | 2 +- .../StickerRoomTimelineView.swift | 6 +- .../TextRoomTimelineView.swift | 4 +- .../UnsupportedRoomTimelineView.swift | 2 +- .../VideoRoomTimelineView.swift | 6 +- .../Fixtures/RoomTimelineItemFixtures.swift | 40 +++++------ .../MockRoomTimelineController.swift | 5 +- .../RoomTimelineController.swift | 52 +++++---------- .../RoomTimelineControllerProtocol.swift | 16 +---- .../Timeline/TimelineItemIdentifier.swift | 66 +++++++++++++++++++ .../Services/Timeline/TimelineItemProxy.swift | 36 +--------- .../VoiceMessageRoomPlaybackView.swift | 2 +- .../VoiceMessageRoomTimelineView.swift | 2 +- .../PaginationIndicatorRoomTimelineItem.swift | 2 +- .../TimelineStartRoomTimelineItem.swift | 2 +- .../Services/Timeline/TimelineProxy.swift | 36 ++++++---- .../Timeline/TimelineProxyProtocol.swift | 17 +---- UnitTests/Sources/AudioPlayerStateTests.swift | 4 +- .../ComposerToolbarViewModelTests.swift | 22 +++---- UnitTests/Sources/LoggingTests.swift | 12 ++-- .../Sources/MediaPlayerProviderTests.swift | 6 +- ...essageForwardingScreenViewModelTests.swift | 2 +- ...dUserSendFailureScreenViewModelTests.swift | 2 +- .../Sources/TextBasedRoomTimelineTests.swift | 8 +-- .../Sources/TimelineViewModelTests.swift | 6 +- 56 files changed, 313 insertions(+), 298 deletions(-) create mode 100644 ElementX/Sources/Services/Timeline/TimelineItemIdentifier.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 1af189e9c8..b1b8124cdd 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -611,6 +611,7 @@ 86F9D3028A1F4AE819D75560 /* RoomChangePermissionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D879FC4E881E748BB9B34DC /* RoomChangePermissionsScreenCoordinator.swift */; }; 872A6457DF573AF8CEAE927A /* LoginHomeserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9349F590E35CE514A71E6764 /* LoginHomeserver.swift */; }; 874FEFB9D4A4AF447E0E086E /* AuthenticationStartScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0F7CCC4A9D1927223F559D5 /* AuthenticationStartScreenViewModelProtocol.swift */; }; + 877D3CE8680536DB430DE6A2 /* TimelineItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E48C91C8BE55CAE1A3DBC3BC /* TimelineItemIdentifier.swift */; }; 878070573C7BF19E735707B4 /* RoomTimelineItemProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DE8D25D6A91030175D52A20 /* RoomTimelineItemProperties.swift */; }; 87B4E59A4467F8EC75F82372 /* VoiceMessageRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B70A50C41C5871B4DB905E7E /* VoiceMessageRoomTimelineView.swift */; }; 87CEA3E07B602705BC2D2A20 /* ClientBuilderHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC0CD1CAFD3F8B057F9AEA5 /* ClientBuilderHook.swift */; }; @@ -2189,6 +2190,7 @@ E44E35AA87F49503E7B3BF6E /* AudioConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioConverter.swift; sourceTree = ""; }; E45EBAFF1A83538D54ABDF92 /* ServerSelectionScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionScreenViewModelTests.swift; sourceTree = ""; }; E461B3C8BBBFCA400B268D14 /* AppRouteURLParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouteURLParserTests.swift; sourceTree = ""; }; + E48C91C8BE55CAE1A3DBC3BC /* TimelineItemIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemIdentifier.swift; sourceTree = ""; }; E5272BC4A60B6AD7553BACA1 /* BlurHashDecode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashDecode.swift; sourceTree = ""; }; E53BFB7E4F329621C844E8C3 /* AnalyticsPromptScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptScreen.swift; sourceTree = ""; }; E55B5EA766E89FF1F87C3ACB /* RoomNotificationSettingsProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsProxyProtocol.swift; sourceTree = ""; }; @@ -5473,6 +5475,7 @@ 0DF5CBAF69BDF5DF31C661E1 /* IntentionalMentions.swift */, 66F2402D738694F98729A441 /* RoomTimelineProvider.swift */, 095AED4CF56DFF3EB7BB84C8 /* RoomTimelineProviderProtocol.swift */, + E48C91C8BE55CAE1A3DBC3BC /* TimelineItemIdentifier.swift */, 2D505843AB66822EB91F0DF0 /* TimelineItemProxy.swift */, 55AEEF8142DF1B59DB40FB93 /* TimelineItemSender.swift */, F9E543072DE58E751F028998 /* TimelineProxy.swift */, @@ -6931,6 +6934,7 @@ E6FA87F773424B27614B23E9 /* TimelineItemAccessibilityModifier.swift in Sources */, 79959F8E45C3749997482A7F /* TimelineItemBubbledStylerView.swift in Sources */, A808DC3F72D15C6C5A52317E /* TimelineItemDebugView.swift in Sources */, + 877D3CE8680536DB430DE6A2 /* TimelineItemIdentifier.swift in Sources */, C0B97FFEC0083F3A36609E61 /* TimelineItemMacContextMenu.swift in Sources */, 6C98153D60FF9B648C166C27 /* TimelineItemMenu.swift in Sources */, AE07F215EBC2B9CBF17AA54B /* TimelineItemMenuAction.swift in Sources */, diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index 5652a91f2f..75fa3da1d3 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -334,6 +334,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg switch await roomProxy.timeline.sendMessage(replyText, html: nil, + inReplyToEventID: nil, intentionalMentions: .empty) { case .success: break diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index db66e04c9c..e3c0bace6a 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -13987,8 +13987,8 @@ class TimelineProxyMock: TimelineProxyProtocol { var editNewContentCalled: Bool { return editNewContentCallsCount > 0 } - var editNewContentReceivedArguments: (timelineItem: EventTimelineItem, newContent: RoomMessageEventContentWithoutRelation)? - var editNewContentReceivedInvocations: [(timelineItem: EventTimelineItem, newContent: RoomMessageEventContentWithoutRelation)] = [] + var editNewContentReceivedArguments: (eventOrTransactionID: EventOrTransactionId, newContent: RoomMessageEventContentWithoutRelation)? + var editNewContentReceivedInvocations: [(eventOrTransactionID: EventOrTransactionId, newContent: RoomMessageEventContentWithoutRelation)] = [] var editNewContentUnderlyingReturnValue: Result! var editNewContentReturnValue: Result! { @@ -14014,16 +14014,16 @@ class TimelineProxyMock: TimelineProxyProtocol { } } } - var editNewContentClosure: ((EventTimelineItem, RoomMessageEventContentWithoutRelation) async -> Result)? + var editNewContentClosure: ((EventOrTransactionId, RoomMessageEventContentWithoutRelation) async -> Result)? - func edit(_ timelineItem: EventTimelineItem, newContent: RoomMessageEventContentWithoutRelation) async -> Result { + func edit(_ eventOrTransactionID: EventOrTransactionId, newContent: RoomMessageEventContentWithoutRelation) async -> Result { editNewContentCallsCount += 1 - editNewContentReceivedArguments = (timelineItem: timelineItem, newContent: newContent) + editNewContentReceivedArguments = (eventOrTransactionID: eventOrTransactionID, newContent: newContent) DispatchQueue.main.async { - self.editNewContentReceivedInvocations.append((timelineItem: timelineItem, newContent: newContent)) + self.editNewContentReceivedInvocations.append((eventOrTransactionID: eventOrTransactionID, newContent: newContent)) } if let editNewContentClosure = editNewContentClosure { - return await editNewContentClosure(timelineItem, newContent) + return await editNewContentClosure(eventOrTransactionID, newContent) } else { return editNewContentReturnValue } @@ -14770,15 +14770,15 @@ class TimelineProxyMock: TimelineProxyProtocol { } //MARK: - sendMessage - var sendMessageHtmlInReplyToIntentionalMentionsUnderlyingCallsCount = 0 - var sendMessageHtmlInReplyToIntentionalMentionsCallsCount: Int { + var sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingCallsCount = 0 + var sendMessageHtmlInReplyToEventIDIntentionalMentionsCallsCount: Int { get { if Thread.isMainThread { - return sendMessageHtmlInReplyToIntentionalMentionsUnderlyingCallsCount + return sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = sendMessageHtmlInReplyToIntentionalMentionsUnderlyingCallsCount + returnValue = sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingCallsCount } return returnValue! @@ -14786,29 +14786,29 @@ class TimelineProxyMock: TimelineProxyProtocol { } set { if Thread.isMainThread { - sendMessageHtmlInReplyToIntentionalMentionsUnderlyingCallsCount = newValue + sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - sendMessageHtmlInReplyToIntentionalMentionsUnderlyingCallsCount = newValue + sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingCallsCount = newValue } } } } - var sendMessageHtmlInReplyToIntentionalMentionsCalled: Bool { - return sendMessageHtmlInReplyToIntentionalMentionsCallsCount > 0 + var sendMessageHtmlInReplyToEventIDIntentionalMentionsCalled: Bool { + return sendMessageHtmlInReplyToEventIDIntentionalMentionsCallsCount > 0 } - var sendMessageHtmlInReplyToIntentionalMentionsReceivedArguments: (message: String, html: String?, eventID: String?, intentionalMentions: IntentionalMentions)? - var sendMessageHtmlInReplyToIntentionalMentionsReceivedInvocations: [(message: String, html: String?, eventID: String?, intentionalMentions: IntentionalMentions)] = [] + var sendMessageHtmlInReplyToEventIDIntentionalMentionsReceivedArguments: (message: String, html: String?, inReplyToEventID: String?, intentionalMentions: IntentionalMentions)? + var sendMessageHtmlInReplyToEventIDIntentionalMentionsReceivedInvocations: [(message: String, html: String?, inReplyToEventID: String?, intentionalMentions: IntentionalMentions)] = [] - var sendMessageHtmlInReplyToIntentionalMentionsUnderlyingReturnValue: Result! - var sendMessageHtmlInReplyToIntentionalMentionsReturnValue: Result! { + var sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingReturnValue: Result! + var sendMessageHtmlInReplyToEventIDIntentionalMentionsReturnValue: Result! { get { if Thread.isMainThread { - return sendMessageHtmlInReplyToIntentionalMentionsUnderlyingReturnValue + return sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingReturnValue } else { var returnValue: Result? = nil DispatchQueue.main.sync { - returnValue = sendMessageHtmlInReplyToIntentionalMentionsUnderlyingReturnValue + returnValue = sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingReturnValue } return returnValue! @@ -14816,26 +14816,26 @@ class TimelineProxyMock: TimelineProxyProtocol { } set { if Thread.isMainThread { - sendMessageHtmlInReplyToIntentionalMentionsUnderlyingReturnValue = newValue + sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - sendMessageHtmlInReplyToIntentionalMentionsUnderlyingReturnValue = newValue + sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingReturnValue = newValue } } } } - var sendMessageHtmlInReplyToIntentionalMentionsClosure: ((String, String?, String?, IntentionalMentions) async -> Result)? + var sendMessageHtmlInReplyToEventIDIntentionalMentionsClosure: ((String, String?, String?, IntentionalMentions) async -> Result)? - func sendMessage(_ message: String, html: String?, inReplyTo eventID: String?, intentionalMentions: IntentionalMentions) async -> Result { - sendMessageHtmlInReplyToIntentionalMentionsCallsCount += 1 - sendMessageHtmlInReplyToIntentionalMentionsReceivedArguments = (message: message, html: html, eventID: eventID, intentionalMentions: intentionalMentions) + func sendMessage(_ message: String, html: String?, inReplyToEventID: String?, intentionalMentions: IntentionalMentions) async -> Result { + sendMessageHtmlInReplyToEventIDIntentionalMentionsCallsCount += 1 + sendMessageHtmlInReplyToEventIDIntentionalMentionsReceivedArguments = (message: message, html: html, inReplyToEventID: inReplyToEventID, intentionalMentions: intentionalMentions) DispatchQueue.main.async { - self.sendMessageHtmlInReplyToIntentionalMentionsReceivedInvocations.append((message: message, html: html, eventID: eventID, intentionalMentions: intentionalMentions)) + self.sendMessageHtmlInReplyToEventIDIntentionalMentionsReceivedInvocations.append((message: message, html: html, inReplyToEventID: inReplyToEventID, intentionalMentions: intentionalMentions)) } - if let sendMessageHtmlInReplyToIntentionalMentionsClosure = sendMessageHtmlInReplyToIntentionalMentionsClosure { - return await sendMessageHtmlInReplyToIntentionalMentionsClosure(message, html, eventID, intentionalMentions) + if let sendMessageHtmlInReplyToEventIDIntentionalMentionsClosure = sendMessageHtmlInReplyToEventIDIntentionalMentionsClosure { + return await sendMessageHtmlInReplyToEventIDIntentionalMentionsClosure(message, html, inReplyToEventID, intentionalMentions) } else { - return sendMessageHtmlInReplyToIntentionalMentionsReturnValue + return sendMessageHtmlInReplyToEventIDIntentionalMentionsReturnValue } } //MARK: - toggleReaction diff --git a/ElementX/Sources/Mocks/PollMock.swift b/ElementX/Sources/Mocks/PollMock.swift index 0da15ef331..821550f415 100644 --- a/ElementX/Sources/Mocks/PollMock.swift +++ b/ElementX/Sources/Mocks/PollMock.swift @@ -82,7 +82,7 @@ extension Poll.Option { extension PollRoomTimelineItem { static func mock(poll: Poll, isOutgoing: Bool = true, isEditable: Bool = false) -> Self { - .init(id: .random, + .init(id: .randomEvent, poll: poll, body: "poll", timestamp: "Now", diff --git a/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift b/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift index 8dc876e85b..2801f66ef2 100644 --- a/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift +++ b/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift @@ -93,7 +93,7 @@ private struct MessageForwardingListRow: View { struct MessageForwardingScreen_Previews: PreviewProvider, TestablePreview { static var previews: some View { let summaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))) - let viewModel = MessageForwardingScreenViewModel(forwardingItem: .init(id: .init(uniqueID: ""), + let viewModel = MessageForwardingScreenViewModel(forwardingItem: .init(id: .randomEvent, roomID: "", content: .init(noPointer: .init())), clientProxy: ClientProxyMock(.init()), diff --git a/ElementX/Sources/Screens/ResolveVerifiedUserSendFailureScreen/View/ResolveVerifiedUserSendFailureScreen.swift b/ElementX/Sources/Screens/ResolveVerifiedUserSendFailureScreen/View/ResolveVerifiedUserSendFailureScreen.swift index 2d2df80a60..2964e317ba 100644 --- a/ElementX/Sources/Screens/ResolveVerifiedUserSendFailureScreen/View/ResolveVerifiedUserSendFailureScreen.swift +++ b/ElementX/Sources/Screens/ResolveVerifiedUserSendFailureScreen/View/ResolveVerifiedUserSendFailureScreen.swift @@ -94,7 +94,7 @@ struct ResolveVerifiedUserSendFailureScreen_Previews: PreviewProvider, TestableP static func makeViewModel(failure: TimelineItemSendFailure.VerifiedUser) -> ResolveVerifiedUserSendFailureScreenViewModel { ResolveVerifiedUserSendFailureScreenViewModel(failure: failure, - itemID: .random, + itemID: .randomEvent, roomProxy: JoinedRoomProxyMock(.init()), userIndicatorController: UserIndicatorControllerMock()) } @@ -102,7 +102,7 @@ struct ResolveVerifiedUserSendFailureScreen_Previews: PreviewProvider, TestableP struct ResolveVerifiedUserSendFailureScreenSheet_Previews: PreviewProvider { static let viewModel = ResolveVerifiedUserSendFailureScreenViewModel(failure: .changedIdentity(users: ["@alice:matrix.org"]), - itemID: .random, + itemID: .randomEvent, roomProxy: JoinedRoomProxyMock(.init()), userIndicatorController: UserIndicatorControllerMock()) diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift index 9f3f169bfb..8f5fc54e37 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift @@ -6,6 +6,7 @@ // import Compound +import MatrixRustSDK import SwiftUI import WysiwygComposer @@ -283,9 +284,14 @@ extension FormatType { } enum ComposerMode: Equatable { + enum EditSource { + case timeline + case draftService + } + case `default` - case reply(itemID: TimelineItemIdentifier, replyDetails: TimelineItemReplyDetails, isThread: Bool) - case edit(originalItemId: TimelineItemIdentifier) + case reply(eventID: String, replyDetails: TimelineItemReplyDetails, isThread: Bool) + case edit(originalEventOrTransactionID: EventOrTransactionId, source: EditSource) case recordVoiceMessage(state: AudioRecorderState) case previewVoiceMessage(state: AudioPlayerState, waveform: WaveformSource, isUploading: Bool) @@ -323,8 +329,8 @@ enum ComposerMode: Equatable { var replyEventID: String? { switch self { - case .reply(let itemID, _, _): - return itemID.eventID + case .reply(let eventID, _, _): + return eventID default: return nil } diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift index 970ee05be9..c15a17b65b 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift @@ -258,9 +258,9 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool case .newMessage: set(mode: .default) case .edit(let eventID): - set(mode: .edit(originalItemId: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: eventID)))) + set(mode: .edit(originalEventOrTransactionID: .eventId(eventId: eventID), source: .draftService)) case .reply(let eventID): - set(mode: .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: eventID)), replyDetails: .loading(eventID: eventID), isThread: false)) + set(mode: .reply(eventID: eventID, replyDetails: .loading(eventID: eventID), isThread: false)) replyLoadingTask = Task { let reply = switch await draftService.getReply(eventID: eventID) { case .success(let reply): @@ -273,7 +273,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool return } - set(mode: .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: eventID)), replyDetails: reply.details, isThread: reply.isThreaded)) + set(mode: .reply(eventID: eventID, replyDetails: reply.details, isThread: reply.isThreaded)) } } } @@ -314,17 +314,9 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool switch state.composerMode { case .default: type = .newMessage - case .edit(let itemID): - guard let eventID = itemID.eventID else { - MXLog.error("The event id for this message is missing") - return - } - type = .edit(eventID: eventID) - case .reply(let itemID, _, _): - guard let eventID = itemID.eventID else { - MXLog.error("The event id for this message is missing") - return - } + case .edit(.eventId(let originalEventID), _): + type = .edit(eventID: originalEventID) + case .reply(let eventID, _, _): type = .reply(eventID: eventID) default: if isVolatile { diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift index c4f4fa384f..8f1dcaa6c6 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift @@ -415,10 +415,10 @@ extension ComposerToolbar { mentionDisplayHelper: ComposerMentionDisplayHelper.mock, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) - model.state.composerMode = isLoading ? .reply(itemID: .init(uniqueID: ""), + model.state.composerMode = isLoading ? .reply(eventID: UUID().uuidString, replyDetails: .loading(eventID: ""), isThread: false) : - .reply(itemID: .init(uniqueID: ""), + .reply(eventID: UUID().uuidString, replyDetails: .loaded(sender: .init(id: "", displayName: "Test"), eventID: "", eventContent: .message(.text(.init(body: "Hello World!")))), isThread: false) diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift index 61a3a6911c..16f2c0f711 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift @@ -275,9 +275,9 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview { messageComposer() messageComposer(.init(string: "Some message"), - mode: .edit(originalItemId: .random)) + mode: .edit(originalEventOrTransactionID: .eventId(eventId: UUID().uuidString), source: .timeline)) - messageComposer(mode: .reply(itemID: .random, + messageComposer(mode: .reply(eventID: UUID().uuidString, replyDetails: .loaded(sender: .init(id: "Kirk"), eventID: "123", eventContent: .message(.text(.init(body: "Text: Where the wild things are")))), @@ -288,7 +288,7 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview { ScrollView { VStack(spacing: 8) { ForEach(replyTypes, id: \.self) { replyDetails in - messageComposer(mode: .reply(itemID: .random, + messageComposer(mode: .reply(eventID: UUID().uuidString, replyDetails: replyDetails, isThread: false)) } } @@ -300,7 +300,7 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview { ScrollView { VStack(spacing: 8) { ForEach(replyTypes, id: \.self) { replyDetails in - messageComposer(mode: .reply(itemID: .random, + messageComposer(mode: .reply(eventID: UUID().uuidString, replyDetails: replyDetails, isThread: true)) } } diff --git a/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift b/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift index 7696f2501a..3bab9b8db4 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift @@ -143,7 +143,7 @@ class TimelineInteractionHandler { let replyInfo = buildReplyInfo(for: eventTimelineItem) let replyDetails = TimelineItemReplyDetails.loaded(sender: eventTimelineItem.sender, eventID: eventID, eventContent: replyInfo.type) - actionsSubject.send(.composer(action: .setMode(mode: .reply(itemID: eventTimelineItem.id, replyDetails: replyDetails, isThread: replyInfo.isThread)))) + actionsSubject.send(.composer(action: .setMode(mode: .reply(eventID: eventID, replyDetails: replyDetails, isThread: replyInfo.isThread)))) case .forward(let itemID): actionsSubject.send(.displayMessageForwarding(itemID: itemID)) case .viewSource: @@ -184,6 +184,11 @@ class TimelineInteractionHandler { } private func processEditMessageEvent(_ messageTimelineItem: EventBasedMessageTimelineItemProtocol) { + guard case let .event(_, eventOrTransactionID) = messageTimelineItem.id else { + MXLog.error("Failed editing message, missing event id") + return + } + let text: String var htmlText: String? switch messageTimelineItem.contentType { @@ -197,7 +202,7 @@ class TimelineInteractionHandler { } // Always update the mode first and then the text so that the composer has time to save the text draft - actionsSubject.send(.composer(action: .setMode(mode: .edit(originalItemId: messageTimelineItem.id)))) + actionsSubject.send(.composer(action: .setMode(mode: .edit(originalEventOrTransactionID: eventOrTransactionID, source: .timeline)))) actionsSubject.send(.composer(action: .setText(plainText: text, htmlText: htmlText))) } diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index 2dc59055fa..bc83d1d71c 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -593,13 +593,14 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { actionsSubject.send(.composer(action: .clear)) switch mode { - case .reply(let itemId, _, _): + case .reply(let eventID, _, _): await timelineController.sendMessage(message, html: html, - inReplyTo: itemId, + inReplyToEventID: eventID, intentionalMentions: intentionalMentions) - case .edit(let originalItemId): - await timelineController.edit(originalItemId, + case .edit(let originalEventOrTransactionID, let source): + await timelineController.edit(originalEventOrTransactionID, + useTimeline: source == .timeline, message: message, html: html, intentionalMentions: intentionalMentions) @@ -610,6 +611,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { case .none: await timelineController.sendMessage(message, html: html, + inReplyToEventID: nil, intentionalMentions: intentionalMentions) } case .recordVoiceMessage, .previewVoiceMessage: diff --git a/ElementX/Sources/Screens/Timeline/View/Style/SwipeToReplyView.swift b/ElementX/Sources/Screens/Timeline/View/Style/SwipeToReplyView.swift index 47b611a66b..476ee8465f 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/SwipeToReplyView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/SwipeToReplyView.swift @@ -19,7 +19,7 @@ struct SwipeToReplyView: View { } struct SwipeToReplyView_Previews: PreviewProvider, TestablePreview { - static let timelineItem = TextRoomTimelineItem(id: .init(uniqueID: ""), + static let timelineItem = TextRoomTimelineItem(id: .randomEvent, timestamp: "", isOutgoing: true, isEditable: true, diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift index 7d809e2cb2..4b6eaf2e46 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift @@ -398,7 +398,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview static var replies: some View { VStack(spacing: 0) { - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .randomEvent, timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -411,7 +411,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview eventContent: .message(.text(.init(body: "Short"))))), groupStyle: .single)) - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .randomEvent, timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -443,7 +443,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview static var encryptionAuthenticity: some View { VStack(spacing: 0) { - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .randomEvent, timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -454,7 +454,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview properties: RoomTimelineItemProperties(encryptionAuthenticity: .unsignedDevice(color: .red))), groupStyle: .single)) - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .randomEvent, timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -466,7 +466,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview encryptionAuthenticity: .unsignedDevice(color: .red))), groupStyle: .single)) - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .randomEvent, timestamp: "10:42", isOutgoing: false, isEditable: false, @@ -477,7 +477,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview properties: RoomTimelineItemProperties(encryptionAuthenticity: .unknownDevice(color: .red))), groupStyle: .first)) - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .randomEvent, timestamp: "10:42", isOutgoing: false, isEditable: false, @@ -488,7 +488,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview properties: RoomTimelineItemProperties(encryptionAuthenticity: .notGuaranteed(color: .gray))), groupStyle: .last)) - ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .random, + ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -501,7 +501,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview properties: RoomTimelineItemProperties(encryptionAuthenticity: .notGuaranteed(color: .gray)))) - VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(uniqueID: ""), + VoiceMessageRoomTimelineView(timelineItem: .init(id: .randomEvent, timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -514,7 +514,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview source: nil, contentType: nil), properties: RoomTimelineItemProperties(encryptionAuthenticity: .notGuaranteed(color: .gray))), - playerState: AudioPlayerState(id: .timelineItemIdentifier(.random), + playerState: AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: L10n.commonVoiceMessage, duration: 10, waveform: EstimatedWaveform.mockWaveform)) @@ -616,14 +616,14 @@ private struct MockTimelineContent: View { source: nil, contentType: nil), replyDetails: replyDetails), - playerState: AudioPlayerState(id: .timelineItemIdentifier(.random), + playerState: AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: L10n.commonVoiceMessage, duration: 10, waveform: EstimatedWaveform.mockWaveform)) } func makeItemIdentifier() -> TimelineItemIdentifier { - isPinned ? .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "pinned")) : .random + isPinned ? .event(uniqueID: "", eventOrTransactionID: .eventId(eventId: "pinned")) : .randomEvent } var replyDetails: TimelineItemReplyDetails? { diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemSendInfoLabel.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemSendInfoLabel.swift index 04e5544a43..d2cbd56391 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemSendInfoLabel.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemSendInfoLabel.swift @@ -180,22 +180,22 @@ private extension EncryptionAuthenticity { struct TimelineItemSendInfoLabel_Previews: PreviewProvider, TestablePreview { static var previews: some View { VStack(spacing: 16) { - TimelineItemSendInfoLabel(sendInfo: .init(itemID: .random, + TimelineItemSendInfoLabel(sendInfo: .init(itemID: .randomEvent, localizedString: "09:47 AM", layoutType: .horizontal())) - TimelineItemSendInfoLabel(sendInfo: .init(itemID: .random, + TimelineItemSendInfoLabel(sendInfo: .init(itemID: .randomEvent, localizedString: "09:47 AM", status: .sendingFailed, layoutType: .horizontal())) - TimelineItemSendInfoLabel(sendInfo: .init(itemID: .random, + TimelineItemSendInfoLabel(sendInfo: .init(itemID: .randomEvent, localizedString: "09:47 AM", status: .encryptionAuthenticity(.unsignedDevice(color: .red)), layoutType: .horizontal())) - TimelineItemSendInfoLabel(sendInfo: .init(itemID: .random, + TimelineItemSendInfoLabel(sendInfo: .init(itemID: .randomEvent, localizedString: "09:47 AM", status: .encryptionAuthenticity(.notGuaranteed(color: .gray)), layoutType: .horizontal())) - TimelineItemSendInfoLabel(sendInfo: .init(itemID: .random, + TimelineItemSendInfoLabel(sendInfo: .init(itemID: .randomEvent, localizedString: "09:47 AM", status: .encryptionAuthenticity(.sentInClear(color: .red)), layoutType: .horizontal())) diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift index ac7dc5d593..820cdd6423 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift @@ -57,7 +57,7 @@ struct TimelineStyler: View { struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { static let viewModel = TimelineViewModel.mock - static let base = TextRoomTimelineItem(id: .random, + static let base = TextRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: true, isEditable: false, @@ -80,7 +80,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { static let sendingLast: TextRoomTimelineItem = { let id = viewModel.state.timelineViewState.uniqueIDs.last ?? UUID().uuidString - var result = TextRoomTimelineItem(id: .init(uniqueID: id), + var result = TextRoomTimelineItem(id: .event(uniqueID: id, eventOrTransactionID: .eventId(eventId: UUID().uuidString)), timestamp: "Now", isOutgoing: true, isEditable: false, @@ -100,7 +100,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { static let sentLast: TextRoomTimelineItem = { let id = viewModel.state.timelineViewState.uniqueIDs.last ?? UUID().uuidString - let result = TextRoomTimelineItem(id: .init(uniqueID: id), + let result = TextRoomTimelineItem(id: .event(uniqueID: id, eventOrTransactionID: .eventId(eventId: UUID().uuidString)), timestamp: "Now", isOutgoing: true, isEditable: false, @@ -111,7 +111,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { return result }() - static let ltrString = TextRoomTimelineItem(id: .random, + static let ltrString = TextRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: true, isEditable: false, @@ -119,7 +119,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { isThreaded: false, sender: .test, content: .init(body: "house!")) - static let rtlString = TextRoomTimelineItem(id: .random, + static let rtlString = TextRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: true, isEditable: false, @@ -127,7 +127,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { isThreaded: false, sender: .test, content: .init(body: "באמת!")) - static let ltrStringThatContainsRtl = TextRoomTimelineItem(id: .random, + static let ltrStringThatContainsRtl = TextRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: true, isEditable: false, @@ -136,7 +136,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { sender: .test, content: .init(body: "house! -- באמת‏! -- house!")) - static let rtlStringThatContainsLtr = TextRoomTimelineItem(id: .random, + static let rtlStringThatContainsLtr = TextRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: true, isEditable: false, @@ -145,7 +145,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { sender: .test, content: .init(body: "באמת‏! -- house! -- באמת!")) - static let ltrStringThatFinishesInRtl = TextRoomTimelineItem(id: .random, + static let ltrStringThatFinishesInRtl = TextRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: true, isEditable: false, @@ -154,7 +154,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { sender: .test, content: .init(body: "house! -- באמת!")) - static let rtlStringThatFinishesInLtr = TextRoomTimelineItem(id: .random, + static let rtlStringThatFinishesInLtr = TextRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: true, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReactionsView.swift b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReactionsView.swift index f8656fdf23..a49631c3ba 100644 --- a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReactionsView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReactionsView.swift @@ -196,20 +196,20 @@ struct TimelineReactionViewPreviewsContainer: View { var body: some View { VStack(spacing: 8) { TimelineReactionsView(context: TimelineViewModel.mock.context, - itemID: .init(uniqueID: "1"), + itemID: .randomEvent, reactions: [AggregatedReaction.mockReactionWithLongText, AggregatedReaction.mockReactionWithLongTextRTL]) Divider() TimelineReactionsView(context: TimelineViewModel.mock.context, - itemID: .init(uniqueID: "2"), + itemID: .randomEvent, reactions: Array(AggregatedReaction.mockReactions.prefix(3))) Divider() TimelineReactionsView(context: TimelineViewModel.mock.context, - itemID: .init(uniqueID: "3"), + itemID: .randomEvent, reactions: AggregatedReaction.mockReactions) Divider() TimelineReactionsView(context: TimelineViewModel.mock.context, - itemID: .init(uniqueID: "4"), + itemID: .randomEvent, reactions: AggregatedReaction.mockReactions, isLayoutRTL: true) } diff --git a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift index ddf1c9b63e..f3f88fedd1 100644 --- a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift @@ -103,7 +103,7 @@ struct TimelineReadReceiptsView_Previews: PreviewProvider, TestablePreview { ReadReceipt(userID: RoomMemberProxyMock.mockDan.userID, formattedTimestamp: "Way, way before")] static func mockTimelineItem(with receipts: [ReadReceipt]) -> TextRoomTimelineItem { - TextRoomTimelineItem(id: .random, + TextRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: true, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/AudioRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/AudioRoomTimelineView.swift index 59952d1185..b89ec72f76 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/AudioRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/AudioRoomTimelineView.swift @@ -34,7 +34,7 @@ struct AudioRoomTimelineView_Previews: PreviewProvider, TestablePreview { } static var body: some View { - AudioRoomTimelineView(timelineItem: AudioRoomTimelineItem(id: .random, + AudioRoomTimelineView(timelineItem: AudioRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallInviteRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallInviteRoomTimelineView.swift index bb0406cb74..3beab6197c 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallInviteRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallInviteRoomTimelineView.swift @@ -30,7 +30,7 @@ struct CallInviteRoomTimelineView_Previews: PreviewProvider, TestablePreview { } static var body: some View { - CallInviteRoomTimelineView(timelineItem: .init(id: .random, + CallInviteRoomTimelineView(timelineItem: .init(id: .randomEvent, timestamp: "Now", isEditable: false, canBeRepliedTo: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallNotificationRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallNotificationRoomTimelineView.swift index f3eefcd1af..c584b8a34f 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallNotificationRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallNotificationRoomTimelineView.swift @@ -60,7 +60,7 @@ struct CallNotificationRoomTimelineView_Previews: PreviewProvider, TestablePrevi } static var body: some View { - CallNotificationRoomTimelineView(timelineItem: .init(id: .random, + CallNotificationRoomTimelineView(timelineItem: .init(id: .randomEvent, timestamp: "Now", isEditable: false, canBeRepliedTo: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift index fd50654fe1..03c212504d 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift @@ -52,8 +52,8 @@ struct CollapsibleRoomTimelineView: View { struct CollapsibleRoomTimelineView_Previews: PreviewProvider, TestablePreview { static let item = CollapsibleTimelineItem(items: [ - SeparatorRoomTimelineItem(id: .init(uniqueID: "First separator"), text: "This is a separator"), - SeparatorRoomTimelineItem(id: .init(uniqueID: "Second separator"), text: "This is another separator") + SeparatorRoomTimelineItem(id: .virtual(uniqueID: "First separator"), text: "This is a separator"), + SeparatorRoomTimelineItem(id: .virtual(uniqueID: "Second separator"), text: "This is another separator") ]) static var previews: some View { diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EmoteRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EmoteRoomTimelineView.swift index 77ce3bec5a..3fd741ccd4 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EmoteRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EmoteRoomTimelineView.swift @@ -42,7 +42,7 @@ struct EmoteRoomTimelineView_Previews: PreviewProvider, TestablePreview { } private static func itemWith(text: String, timestamp: String, senderId: String) -> EmoteRoomTimelineItem { - EmoteRoomTimelineItem(id: .random, + EmoteRoomTimelineItem(id: .randomEvent, timestamp: timestamp, isOutgoing: false, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EncryptedRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EncryptedRoomTimelineView.swift index 5a69efe464..ff3f5d3697 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EncryptedRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EncryptedRoomTimelineView.swift @@ -77,7 +77,7 @@ struct EncryptedRoomTimelineView_Previews: PreviewProvider, TestablePreview { } private static func itemWith(text: String, timestamp: String, isOutgoing: Bool, senderId: String) -> EncryptedRoomTimelineItem { - EncryptedRoomTimelineItem(id: .random, + EncryptedRoomTimelineItem(id: .randomEvent, body: text, encryptionType: .unknown, timestamp: timestamp, @@ -88,7 +88,7 @@ struct EncryptedRoomTimelineView_Previews: PreviewProvider, TestablePreview { } private static func expectedItemWith(timestamp: String, isOutgoing: Bool, senderId: String) -> EncryptedRoomTimelineItem { - EncryptedRoomTimelineItem(id: .random, + EncryptedRoomTimelineItem(id: .randomEvent, body: L10n.commonUnableToDecryptNoAccess, encryptionType: .megolmV1AesSha2(sessionID: "foo", cause: .membership), timestamp: timestamp, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/FileRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/FileRoomTimelineView.swift index a75a139b38..d9b3ccd4d5 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/FileRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/FileRoomTimelineView.swift @@ -35,7 +35,7 @@ struct FileRoomTimelineView_Previews: PreviewProvider, TestablePreview { static var body: some View { VStack(spacing: 20.0) { - FileRoomTimelineView(timelineItem: FileRoomTimelineItem(id: .random, + FileRoomTimelineView(timelineItem: FileRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -47,7 +47,7 @@ struct FileRoomTimelineView_Previews: PreviewProvider, TestablePreview { thumbnailSource: nil, contentType: nil))) - FileRoomTimelineView(timelineItem: FileRoomTimelineItem(id: .random, + FileRoomTimelineView(timelineItem: FileRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -59,7 +59,7 @@ struct FileRoomTimelineView_Previews: PreviewProvider, TestablePreview { thumbnailSource: nil, contentType: nil))) - FileRoomTimelineView(timelineItem: FileRoomTimelineItem(id: .random, + FileRoomTimelineView(timelineItem: FileRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift index 9c8c2471a6..e86d30f13f 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift @@ -52,7 +52,7 @@ struct ImageRoomTimelineView_Previews: PreviewProvider, TestablePreview { static var body: some View { VStack(spacing: 20.0) { - ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .random, + ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -63,7 +63,7 @@ struct ImageRoomTimelineView_Previews: PreviewProvider, TestablePreview { source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/jpg"), thumbnailSource: nil))) - ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .random, + ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -74,7 +74,7 @@ struct ImageRoomTimelineView_Previews: PreviewProvider, TestablePreview { source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), thumbnailSource: nil))) - ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .random, + ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/LocationRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/LocationRoomTimelineView.swift index 8c32b487e3..6d3752dbd2 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/LocationRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/LocationRoomTimelineView.swift @@ -86,7 +86,7 @@ struct LocationRoomTimelineView_Previews: PreviewProvider, TestablePreview { @ViewBuilder static var states: some View { - LocationRoomTimelineView(timelineItem: .init(id: .random, + LocationRoomTimelineView(timelineItem: .init(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -95,7 +95,7 @@ struct LocationRoomTimelineView_Previews: PreviewProvider, TestablePreview { sender: .init(id: "Bob"), content: .init(body: "Fallback geo uri description"))) - LocationRoomTimelineView(timelineItem: .init(id: .random, + LocationRoomTimelineView(timelineItem: .init(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -104,7 +104,7 @@ struct LocationRoomTimelineView_Previews: PreviewProvider, TestablePreview { sender: .init(id: "Bob"), content: .init(body: "Fallback geo uri description", geoURI: .init(latitude: 41.902782, longitude: 12.496366), description: "Location description description description description description description description description"))) - LocationRoomTimelineView(timelineItem: .init(id: .random, + LocationRoomTimelineView(timelineItem: .init(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/NoticeRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/NoticeRoomTimelineView.swift index 9fe8f8fbba..098d8b93cd 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/NoticeRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/NoticeRoomTimelineView.swift @@ -54,7 +54,7 @@ struct NoticeRoomTimelineView_Previews: PreviewProvider, TestablePreview { } private static func itemWith(text: String, timestamp: String, senderId: String) -> NoticeRoomTimelineItem { - NoticeRoomTimelineItem(id: .random, + NoticeRoomTimelineItem(id: .randomEvent, timestamp: timestamp, isOutgoing: false, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift index 71a3b1a0f3..8bbfaf7870 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift @@ -29,12 +29,12 @@ struct ReadMarkerRoomTimelineView: View { struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview { static let viewModel = TimelineViewModel.mock - static let item = ReadMarkerRoomTimelineItem(id: .init(uniqueID: .init(UUID().uuidString))) + static let item = ReadMarkerRoomTimelineItem(id: .randomVirtual) static var previews: some View { VStack(alignment: .leading, spacing: 0) { - RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .init(uniqueID: "Separator"), text: "Today")), groupStyle: .single)) - RoomTimelineItemView(viewState: .init(type: .text(.init(id: .random, + RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: "Separator"), text: "Today")), groupStyle: .single)) + RoomTimelineItemView(viewState: .init(type: .text(.init(id: .randomEvent, timestamp: "", isOutgoing: true, isEditable: false, @@ -45,8 +45,8 @@ struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview { ReadMarkerRoomTimelineView(timelineItem: item) - RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .init(uniqueID: "Separator"), text: "Today")), groupStyle: .single)) - RoomTimelineItemView(viewState: .init(type: .text(.init(id: .random, + RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: "Separator"), text: "Today")), groupStyle: .single)) + RoomTimelineItemView(viewState: .init(type: .text(.init(id: .randomEvent, timestamp: "", isOutgoing: false, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/RedactedRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/RedactedRoomTimelineView.swift index 043c80b88c..81274a73f1 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/RedactedRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/RedactedRoomTimelineView.swift @@ -33,7 +33,7 @@ struct RedactedRoomTimelineView_Previews: PreviewProvider, TestablePreview { } private static func itemWith(text: String, timestamp: String, senderId: String) -> RedactedRoomTimelineItem { - RedactedRoomTimelineItem(id: .random, + RedactedRoomTimelineItem(id: .randomEvent, body: text, timestamp: timestamp, isOutgoing: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift index 13c5068149..3c6be1189c 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift @@ -23,7 +23,8 @@ struct SeparatorRoomTimelineView: View { struct SeparatorRoomTimelineView_Previews: PreviewProvider, TestablePreview { static var previews: some View { - let item = SeparatorRoomTimelineItem(id: .init(uniqueID: "Separator"), text: "This is a separator") + let item = SeparatorRoomTimelineItem(id: .virtual(uniqueID: "Separator"), + text: "This is a separator") SeparatorRoomTimelineView(timelineItem: item) } } diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StateRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StateRoomTimelineView.swift index 47d1a0e055..636e89a513 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StateRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StateRoomTimelineView.swift @@ -30,7 +30,7 @@ struct StateRoomTimelineView_Previews: PreviewProvider, TestablePreview { StateRoomTimelineView(timelineItem: item) } - static let item = StateRoomTimelineItem(id: .random, + static let item = StateRoomTimelineItem(id: .randomVirtual, body: "Alice joined", timestamp: "Now", isOutgoing: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift index 2d9a17f3c8..65f155f53a 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift @@ -43,7 +43,7 @@ struct StickerRoomTimelineView_Previews: PreviewProvider, TestablePreview { static var body: some View { VStack(spacing: 20.0) { - StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: .random, + StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: .randomEvent, body: "Some image", timestamp: "Now", isOutgoing: false, @@ -52,7 +52,7 @@ struct StickerRoomTimelineView_Previews: PreviewProvider, TestablePreview { sender: .init(id: "Bob"), imageURL: URL.picturesDirectory)) - StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: .random, + StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: .randomEvent, body: "Some other image", timestamp: "Now", isOutgoing: false, @@ -61,7 +61,7 @@ struct StickerRoomTimelineView_Previews: PreviewProvider, TestablePreview { sender: .init(id: "Bob"), imageURL: URL.picturesDirectory)) - StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: .random, + StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: .randomEvent, body: "Blurhashed image", timestamp: "Now", isOutgoing: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/TextRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/TextRoomTimelineView.swift index 9c0b6cecac..ec853d579b 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/TextRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/TextRoomTimelineView.swift @@ -80,7 +80,7 @@ struct TextRoomTimelineView_Previews: PreviewProvider, TestablePreview { } private static func itemWith(text: String, timestamp: String, isOutgoing: Bool, senderId: String) -> TextRoomTimelineItem { - TextRoomTimelineItem(id: .random, + TextRoomTimelineItem(id: .randomEvent, timestamp: timestamp, isOutgoing: isOutgoing, isEditable: isOutgoing, @@ -94,7 +94,7 @@ struct TextRoomTimelineView_Previews: PreviewProvider, TestablePreview { let builder = AttributedStringBuilder(cacheKey: "preview", mentionBuilder: MentionBuilder()) let attributedString = builder.fromHTML(html) - return TextRoomTimelineItem(id: .random, + return TextRoomTimelineItem(id: .randomEvent, timestamp: timestamp, isOutgoing: isOutgoing, isEditable: isOutgoing, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/UnsupportedRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/UnsupportedRoomTimelineView.swift index 0f5da6dee3..2d26302ba5 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/UnsupportedRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/UnsupportedRoomTimelineView.swift @@ -52,7 +52,7 @@ struct UnsupportedRoomTimelineView_Previews: PreviewProvider, TestablePreview { } private static func itemWith(text: String, timestamp: String, isOutgoing: Bool, senderId: String) -> UnsupportedRoomTimelineItem { - UnsupportedRoomTimelineItem(id: .random, + UnsupportedRoomTimelineItem(id: .randomEvent, body: text, eventType: "Some Event Type", error: "Something went wrong", diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift index fd530ff938..a99980d85b 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift @@ -63,7 +63,7 @@ struct VideoRoomTimelineView_Previews: PreviewProvider, TestablePreview { static var body: some View { VStack(spacing: 20.0) { - VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .random, + VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -75,7 +75,7 @@ struct VideoRoomTimelineView_Previews: PreviewProvider, TestablePreview { source: nil, thumbnailSource: nil))) - VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .random, + VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -87,7 +87,7 @@ struct VideoRoomTimelineView_Previews: PreviewProvider, TestablePreview { source: nil, thumbnailSource: nil))) - VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .random, + VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, diff --git a/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift b/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift index ff84de4355..dda6752769 100644 --- a/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift +++ b/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift @@ -10,9 +10,9 @@ import Foundation enum RoomTimelineItemFixtures { /// The default timeline items used in Xcode previews etc. static var `default`: [RoomTimelineItemProtocol] = [ - SeparatorRoomTimelineItem(id: .init(uniqueID: "Yesterday"), text: "Yesterday"), - TextRoomTimelineItem(id: .init(uniqueID: ".RoomTimelineItemFixtures.default.0", - eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.0")), + SeparatorRoomTimelineItem(id: .virtual(uniqueID: "Yesterday"), text: "Yesterday"), + TextRoomTimelineItem(id: .event(uniqueID: ".RoomTimelineItemFixtures.default.0", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.0")), timestamp: "10:10 AM", isOutgoing: false, isEditable: false, @@ -21,8 +21,8 @@ enum RoomTimelineItemFixtures { sender: .init(id: "", displayName: "Jacob"), content: .init(body: "That looks so good!"), properties: RoomTimelineItemProperties(isEdited: true)), - TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.1", - eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.1")), + TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.1", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.1")), timestamp: "10:11 AM", isOutgoing: false, isEditable: false, @@ -33,8 +33,8 @@ enum RoomTimelineItemFixtures { properties: RoomTimelineItemProperties(reactions: [ AggregatedReaction(accountOwnerID: "me", key: "🙌", senders: [ReactionSender(id: "me", timestamp: Date())]) ])), - TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.2", - eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.2")), + TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.2", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.2")), timestamp: "10:11 AM", isOutgoing: false, isEditable: false, @@ -52,9 +52,9 @@ enum RoomTimelineItemFixtures { ReactionSender(id: "jacob", timestamp: Date()) ]) ])), - SeparatorRoomTimelineItem(id: .init(uniqueID: "Today"), text: "Today"), - TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.3", - eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.3")), + SeparatorRoomTimelineItem(id: .virtual(uniqueID: "Today"), text: "Today"), + TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.3", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.3")), timestamp: "5 PM", isOutgoing: false, isEditable: false, @@ -63,8 +63,8 @@ enum RoomTimelineItemFixtures { sender: .init(id: "", displayName: "Helena"), content: .init(body: "Wow, cool. Ok, lets go the usual place tomorrow?! Is that too soon? Here’s the menu, let me know what you want it’s on me!"), properties: RoomTimelineItemProperties(orderedReadReceipts: [ReadReceipt(userID: "alice", formattedTimestamp: nil)])), - TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.4", - eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.4")), + TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.4", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.4")), timestamp: "5 PM", isOutgoing: true, isEditable: true, @@ -72,8 +72,8 @@ enum RoomTimelineItemFixtures { isThreaded: false, sender: .init(id: "", displayName: "Bob"), content: .init(body: "And John's speech was amazing!")), - TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.5", - eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.5")), + TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.5", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.5")), timestamp: "5 PM", isOutgoing: true, isEditable: true, @@ -86,8 +86,8 @@ enum RoomTimelineItemFixtures { ReadReceipt(userID: "bob", formattedTimestamp: nil), ReadReceipt(userID: "charlie", formattedTimestamp: nil), ReadReceipt(userID: "dan", formattedTimestamp: nil)])), - TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.6", - eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.6")), + TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.6", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.6")), timestamp: "5 PM", isOutgoing: false, isEditable: false, @@ -242,7 +242,7 @@ enum RoomTimelineItemFixtures { static var permalinkChunk: [RoomTimelineItemProtocol] { (1...20).map { index in - TextRoomTimelineItem(id: .init(uniqueID: "\(index)", eventOrTransactionID: .eventId(eventId: "$\(index)")), + TextRoomTimelineItem(id: .event(uniqueID: "\(index)", eventOrTransactionID: .eventId(eventId: "$\(index)")), text: "Message ID \(index)", senderDisplayName: index > 10 ? "Alice" : "Bob") } @@ -250,7 +250,7 @@ enum RoomTimelineItemFixtures { static var mediaChunk: [RoomTimelineItemProtocol] { [ - VideoRoomTimelineItem(id: .random, + VideoRoomTimelineItem(id: .randomEvent, timestamp: "10:47 am", isOutgoing: false, isEditable: false, @@ -265,7 +265,7 @@ enum RoomTimelineItemFixtures { height: 1080, aspectRatio: 1.78, blurhash: "KtI~70X5V?yss9oyrYs:t6")), - ImageRoomTimelineItem(id: .random, + ImageRoomTimelineItem(id: .randomEvent, timestamp: "10:47 am", isOutgoing: false, isEditable: false, @@ -285,7 +285,7 @@ enum RoomTimelineItemFixtures { private extension TextRoomTimelineItem { init(id: TimelineItemIdentifier? = nil, text: String, senderDisplayName: String) { - self.init(id: id ?? .random, + self.init(id: id ?? .randomEvent, timestamp: "10:47 am", isOutgoing: senderDisplayName == "Alice", isEditable: false, diff --git a/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift index d4844c8810..155989b151 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift @@ -81,12 +81,13 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol { func sendMessage(_ message: String, html: String?, - inReplyTo itemID: TimelineItemIdentifier?, + inReplyToEventID: String?, intentionalMentions: IntentionalMentions) async { } func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async { } - func edit(_ timelineItemID: TimelineItemIdentifier, + func edit(_ eventOrTransactionID: EventOrTransactionId, + useTimeline: Bool, message: String, html: String?, intentionalMentions: IntentionalMentions) async { } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift index cf0381f3a6..0ef0dbd1d6 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift @@ -143,22 +143,13 @@ class RoomTimelineController: RoomTimelineControllerProtocol { func sendMessage(_ message: String, html: String?, - inReplyTo itemID: TimelineItemIdentifier?, + inReplyToEventID: String?, intentionalMentions: IntentionalMentions) async { - var inReplyTo: String? - if itemID == nil { - MXLog.info("Send message in \(roomID)") - } else if let eventID = itemID?.eventID { - inReplyTo = eventID - MXLog.info("Send reply in \(roomID)") - } else { - MXLog.error("Send reply in \(roomID) failed: missing event ID") - return - } + MXLog.info("Send message in \(roomID)") switch await activeTimeline.sendMessage(message, html: html, - inReplyTo: inReplyTo, + inReplyToEventID: inReplyToEventID, intentionalMentions: intentionalMentions) { case .success: MXLog.info("Finished sending message") @@ -178,37 +169,31 @@ class RoomTimelineController: RoomTimelineControllerProtocol { } } - func edit(_ timelineItemID: TimelineItemIdentifier, + func edit(_ eventOrTransactionID: EventOrTransactionId, + useTimeline: Bool, message: String, html: String?, intentionalMentions: IntentionalMentions) async { MXLog.info("Edit message in \(roomID)") - MXLog.info("Editing timeline item: \(timelineItemID)") - - let editMode: EditMode - if !timelineItemID.uniqueID.isEmpty, - let timelineItem = liveTimelineProvider.itemProxies.firstEventTimelineItemUsingStableID(timelineItemID) { - editMode = .byEvent(timelineItem) - } else if let eventID = timelineItemID.eventID { - editMode = .byID(eventID) - } else { - MXLog.error("Unknown timeline item: \(timelineItemID)") - return - } + MXLog.info("Editing timeline item: \(eventOrTransactionID)") let messageContent = activeTimeline.buildMessageContentFor(message, html: html, intentionalMentions: intentionalMentions.toRustMentions()) - switch editMode { - case let .byEvent(item): - switch await activeTimeline.edit(item, newContent: messageContent) { + if useTimeline { + switch await activeTimeline.edit(eventOrTransactionID, newContent: messageContent) { case .success: MXLog.info("Finished editing message by event") case let .failure(error): MXLog.error("Failed editing message by event with error: \(error)") } - case let .byID(eventID): + } else { + guard case let .eventId(eventID) = eventOrTransactionID else { + MXLog.error("Failed editing message, missing eventID.") + return + } + switch await roomProxy.edit(eventID: eventID, newContent: messageContent) { case .success: MXLog.info("Finished editing message by event ID") @@ -398,9 +383,9 @@ class RoomTimelineController: RoomTimelineControllerProtocol { let date = Date(timeIntervalSince1970: TimeInterval(timestamp / 1000)) let dateString = date.formatted(date: .complete, time: .omitted) - return SeparatorRoomTimelineItem(id: .init(uniqueID: dateString), text: dateString) + return SeparatorRoomTimelineItem(id: .virtual(uniqueID: uniqueID), text: dateString) case .readMarker: - return ReadMarkerRoomTimelineItem(id: .init(uniqueID: uniqueID)) + return ReadMarkerRoomTimelineItem(id: .virtual(uniqueID: uniqueID)) } case .unknown: return nil @@ -453,8 +438,3 @@ class RoomTimelineController: RoomTimelineControllerProtocol { return nil } } - -private enum EditMode { - case byEvent(EventTimelineItem) - case byID(String) -} diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift index 4ea6719dde..be7c635585 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift @@ -48,10 +48,11 @@ protocol RoomTimelineControllerProtocol { func sendMessage(_ message: String, html: String?, - inReplyTo itemID: TimelineItemIdentifier?, + inReplyToEventID: String?, intentionalMentions: IntentionalMentions) async - func edit(_ timelineItemID: TimelineItemIdentifier, + func edit(_ eventOrTransactionID: EventOrTransactionId, + useTimeline: Bool, message: String, html: String?, intentionalMentions: IntentionalMentions) async @@ -72,14 +73,3 @@ protocol RoomTimelineControllerProtocol { func eventTimestamp(for itemID: TimelineItemIdentifier) -> Date? } - -extension RoomTimelineControllerProtocol { - func sendMessage(_ message: String, - html: String?, - intentionalMentions: IntentionalMentions) async { - await sendMessage(message, - html: html, - inReplyTo: nil, - intentionalMentions: intentionalMentions) - } -} diff --git a/ElementX/Sources/Services/Timeline/TimelineItemIdentifier.swift b/ElementX/Sources/Services/Timeline/TimelineItemIdentifier.swift new file mode 100644 index 0000000000..1770dbb493 --- /dev/null +++ b/ElementX/Sources/Services/Timeline/TimelineItemIdentifier.swift @@ -0,0 +1,66 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Foundation +import MatrixRustSDK + +/// A timeline item identifier +/// - uniqueID: Stable id across state changes of the timeline item, it uniquely identifies an item in a timeline. +/// Its value is consistent only per timeline instance, it should **not** be used to identify an item across timeline instances. +/// - eventOrTransactionID: Contains the 2 possible identifiers of an event, either it has a remote event id or +/// a local transaction id, never both or none. +enum TimelineItemIdentifier: Hashable { + case event(uniqueID: String, eventOrTransactionID: EventOrTransactionId) + case virtual(uniqueID: String) + + var uniqueID: String { + switch self { + case .event(let uniqueID, _): + return uniqueID + case .virtual(let uniqueID): + return uniqueID + } + } + + var eventID: String? { + guard case let .event(_, eventOrTransactionID) = self else { + return nil + } + + switch eventOrTransactionID { + case .eventId(let eventID): + return eventID + default: + return nil + } + } + + var transactionID: String? { + guard case let .event(_, eventOrTransactionID) = self else { + return nil + } + + switch eventOrTransactionID { + case .transactionId(let transactionID): + return transactionID + default: + return nil + } + } +} + +// MARK: - Mocks + +extension TimelineItemIdentifier { + static var randomEvent: Self { + .event(uniqueID: UUID().uuidString, eventOrTransactionID: .eventId(eventId: UUID().uuidString)) + } + + static var randomVirtual: Self { + .virtual(uniqueID: UUID().uuidString) + } +} diff --git a/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift b/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift index baced33cb3..ef956e8068 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift @@ -8,40 +8,6 @@ import Foundation import MatrixRustSDK -struct TimelineItemIdentifier: Hashable { - /// Stable id across state changes of the timeline item, it uniquely identifies an item in a timeline. - /// It's value is consistent only per timeline instance, it should **not** be used to identify an item across timeline instances. - let uniqueID: String - - /// Contains the 2 possible identifiers of an event, either it has a remote event id or a local transaction id, never both or none. - var eventOrTransactionID: EventOrTransactionId? - - var eventID: String? { - switch eventOrTransactionID { - case .eventId(let eventId): - return eventId - default: - return nil - } - } - - var transactionID: String? { - switch eventOrTransactionID { - case .transactionId(let transactionId): - return transactionId - default: - return nil - } - } -} - -extension TimelineItemIdentifier { - /// Use only for mocks/tests - static var random: Self { - .init(uniqueID: UUID().uuidString, eventOrTransactionID: .eventId(eventId: UUID().uuidString)) - } -} - /// A light wrapper around timeline items returned from Rust. enum TimelineItemProxy { case event(EventTimelineItemProxy) @@ -108,7 +74,7 @@ class EventTimelineItemProxy { init(item: MatrixRustSDK.EventTimelineItem, uniqueID: String) { self.item = item - id = TimelineItemIdentifier(uniqueID: uniqueID, eventOrTransactionID: item.eventOrTransactionId) + id = .event(uniqueID: uniqueID, eventOrTransactionID: item.eventOrTransactionId) } lazy var deliveryStatus: TimelineItemDeliveryStatus? = { diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift index 0b5d3354b3..1cda7b3bda 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift @@ -97,7 +97,7 @@ struct VoiceMessageRoomPlaybackView_Previews: PreviewProvider, TestablePreview { 294, 131, 19, 2, 3, 3, 1, 2, 0, 0, 0, 0, 0, 0, 0, 3]) - static var playerState = AudioPlayerState(id: .timelineItemIdentifier(.random), + static var playerState = AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: L10n.commonVoiceMessage, duration: 10.0, waveform: waveform, diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift index 06ca0a137c..0e70287ac5 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift @@ -55,7 +55,7 @@ struct VoiceMessageRoomTimelineView: View { struct VoiceMessageRoomTimelineView_Previews: PreviewProvider, TestablePreview { static let viewModel = TimelineViewModel.mock - static let timelineItemIdentifier = TimelineItemIdentifier.random + static let timelineItemIdentifier = TimelineItemIdentifier.randomEvent static let voiceRoomTimelineItem = VoiceMessageRoomTimelineItem(id: timelineItemIdentifier, timestamp: "Now", isOutgoing: false, diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift index 837798cb49..319d024476 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift @@ -22,6 +22,6 @@ struct PaginationIndicatorRoomTimelineItem: DecorationTimelineItemProtocol, Equa } init(position: Position) { - id = TimelineItemIdentifier(uniqueID: position.id) + id = .virtual(uniqueID: position.id) } } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/TimelineStartRoomTimelineItem.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/TimelineStartRoomTimelineItem.swift index 17435d9cce..33a601445f 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/TimelineStartRoomTimelineItem.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/TimelineStartRoomTimelineItem.swift @@ -8,6 +8,6 @@ import Foundation struct TimelineStartRoomTimelineItem: DecorationTimelineItemProtocol, Equatable { - let id = TimelineItemIdentifier(uniqueID: UUID().uuidString) + let id: TimelineItemIdentifier = .virtual(uniqueID: UUID().uuidString) let name: String? } diff --git a/ElementX/Sources/Services/Timeline/TimelineProxy.swift b/ElementX/Sources/Services/Timeline/TimelineProxy.swift index 3d7c7ee806..74fe851ae4 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxy.swift @@ -164,17 +164,17 @@ final class TimelineProxy: TimelineProxyProtocol { } } - func edit(_ timelineItem: EventTimelineItem, newContent: RoomMessageEventContentWithoutRelation) async -> Result { + func edit(_ eventOrTransactionID: EventOrTransactionId, newContent: RoomMessageEventContentWithoutRelation) async -> Result { do { - guard try await timeline.edit(eventOrTransactionId: timelineItem.eventOrTransactionId, newContent: .roomMessage(content: newContent)) == true else { + guard try await timeline.edit(eventOrTransactionId: eventOrTransactionID, newContent: .roomMessage(content: newContent)) == true else { return .failure(.failedEditing) } - MXLog.info("Finished editing timeline item: \(timelineItem.eventOrTransactionId)") + MXLog.info("Finished editing timeline item: \(eventOrTransactionID)") return .success(()) } catch { - MXLog.error("Failed editing timeline item: \(timelineItem.eventOrTransactionId) with error: \(error)") + MXLog.error("Failed editing timeline item: \(eventOrTransactionID) with error: \(error)") return .failure(.sdkError(error)) } } @@ -366,10 +366,10 @@ final class TimelineProxy: TimelineProxyProtocol { func sendMessage(_ message: String, html: String?, - inReplyTo eventID: String? = nil, + inReplyToEventID: String? = nil, intentionalMentions: IntentionalMentions) async -> Result { - if let eventID { - MXLog.info("Sending reply to eventID: \(eventID)") + if let inReplyToEventID { + MXLog.info("Sending reply to eventID: \(inReplyToEventID)") } else { MXLog.info("Sending message") } @@ -379,16 +379,16 @@ final class TimelineProxy: TimelineProxyProtocol { intentionalMentions: intentionalMentions.toRustMentions()) do { - if let eventID { - try await timeline.sendReply(msg: messageContent, eventId: eventID) - MXLog.info("Finished sending reply to eventID: \(eventID)") + if let inReplyToEventID { + try await timeline.sendReply(msg: messageContent, eventId: inReplyToEventID) + MXLog.info("Finished sending reply to eventID: \(inReplyToEventID)") } else { _ = try await timeline.send(msg: messageContent) MXLog.info("Finished sending message") } } catch { - if let eventID { - MXLog.error("Failed sending reply to eventID: \(eventID) with error: \(error)") + if let inReplyToEventID { + MXLog.error("Failed sending reply to eventID: \(inReplyToEventID) with error: \(error)") } else { MXLog.error("Failed sending message with error: \(error)") } @@ -631,4 +631,16 @@ extension Array where Element == TimelineItemProxy { return nil } + + func firstEventTimelineItemUsingEventOrTransactionID(_ eventOrTransactionID: EventOrTransactionId) -> EventTimelineItem? { + for item in self { + if case let .event(eventTimelineItem) = item, + case let .event(_, identifier) = eventTimelineItem.id, + identifier == eventOrTransactionID { + return eventTimelineItem.item + } + } + + return nil + } } diff --git a/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift index c081388487..0f18fa5db0 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift @@ -38,7 +38,8 @@ protocol TimelineProxyProtocol { func paginateBackwards(requestSize: UInt16) async -> Result func paginateForwards(requestSize: UInt16) async -> Result - func edit(_ timelineItem: EventTimelineItem, newContent: RoomMessageEventContentWithoutRelation) async -> Result + func edit(_ eventOrTransactionID: EventOrTransactionId, + newContent: RoomMessageEventContentWithoutRelation) async -> Result func redact(_ timelineItemID: TimelineItemIdentifier, reason: String?) async -> Result @@ -89,12 +90,11 @@ protocol TimelineProxyProtocol { func sendMessage(_ message: String, html: String?, - inReplyTo eventID: String?, + inReplyToEventID: String?, intentionalMentions: IntentionalMentions) async -> Result func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async -> Result - // Polls func createPoll(question: String, answers: [String], pollKind: Poll.Kind) async -> Result func editPoll(original eventID: String, @@ -112,14 +112,3 @@ protocol TimelineProxyProtocol { html: String?, intentionalMentions: Mentions) -> RoomMessageEventContentWithoutRelation } - -extension TimelineProxyProtocol { - func sendMessage(_ message: String, - html: String?, - intentionalMentions: IntentionalMentions) async -> Result { - await sendMessage(message, - html: html, - inReplyTo: nil, - intentionalMentions: intentionalMentions) - } -} diff --git a/UnitTests/Sources/AudioPlayerStateTests.swift b/UnitTests/Sources/AudioPlayerStateTests.swift index 7761de3a2c..469dca9bf8 100644 --- a/UnitTests/Sources/AudioPlayerStateTests.swift +++ b/UnitTests/Sources/AudioPlayerStateTests.swift @@ -38,7 +38,7 @@ class AudioPlayerStateTests: XCTestCase { override func setUp() async throws { audioPlayerActionsSubject = .init() audioPlayerSeekCallsSubject = .init() - audioPlayerState = AudioPlayerState(id: .timelineItemIdentifier(.random), title: "", duration: Self.audioDuration) + audioPlayerState = AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: "", duration: Self.audioDuration) audioPlayerMock = buildAudioPlayerMock() audioPlayerMock.seekToClosure = { [weak self] progress in self?.audioPlayerMock.currentTime = Self.audioDuration * progress @@ -162,7 +162,7 @@ class AudioPlayerStateTests: XCTestCase { func testHandlingAudioPlayerActionDidFinishLoading() async throws { audioPlayerMock.duration = 10.0 - audioPlayerState = AudioPlayerState(id: .timelineItemIdentifier(.random), title: "", duration: 0) + audioPlayerState = AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: "", duration: 0) audioPlayerState.attachAudioPlayer(audioPlayerMock) let deferred = deferFulfillment(audioPlayerState.$playbackState) { action in diff --git a/UnitTests/Sources/ComposerToolbarViewModelTests.swift b/UnitTests/Sources/ComposerToolbarViewModelTests.swift index f869343d32..49aa528bfa 100644 --- a/UnitTests/Sources/ComposerToolbarViewModelTests.swift +++ b/UnitTests/Sources/ComposerToolbarViewModelTests.swift @@ -41,14 +41,14 @@ class ComposerToolbarViewModelTests: XCTestCase { } func testComposerFocus() { - viewModel.process(timelineAction: .setMode(mode: .edit(originalItemId: TimelineItemIdentifier(uniqueID: "mock")))) + viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), source: .timeline))) XCTAssertTrue(viewModel.state.bindings.composerFocused) viewModel.process(timelineAction: .removeFocus) XCTAssertFalse(viewModel.state.bindings.composerFocused) } func testComposerMode() { - let mode: ComposerMode = .edit(originalItemId: TimelineItemIdentifier(uniqueID: "mock")) + let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), source: .timeline) viewModel.process(timelineAction: .setMode(mode: mode)) XCTAssertEqual(viewModel.state.composerMode, mode) viewModel.process(timelineAction: .clear) @@ -56,7 +56,7 @@ class ComposerToolbarViewModelTests: XCTestCase { } func testComposerModeIsPublished() { - let mode: ComposerMode = .edit(originalItemId: TimelineItemIdentifier(uniqueID: "mock")) + let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), source: .timeline) let expectation = expectation(description: "Composer mode is published") let cancellable = viewModel .context @@ -236,7 +236,7 @@ class ComposerToolbarViewModelTests: XCTestCase { } viewModel.context.composerFormattingEnabled = false - viewModel.process(timelineAction: .setMode(mode: .edit(originalItemId: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "testID"))))) + viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: "testID"), source: .timeline))) viewModel.context.plainComposerText = .init(string: "Hello world!") viewModel.saveDraft() @@ -257,7 +257,7 @@ class ComposerToolbarViewModelTests: XCTestCase { } viewModel.context.composerFormattingEnabled = false - viewModel.process(timelineAction: .setMode(mode: .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "testID")), + viewModel.process(timelineAction: .setMode(mode: .reply(eventID: "testID", replyDetails: .loaded(sender: .init(id: ""), eventID: "testID", eventContent: .message(.text(.init(body: "reply text")))), @@ -282,7 +282,7 @@ class ComposerToolbarViewModelTests: XCTestCase { } viewModel.context.composerFormattingEnabled = false - viewModel.process(timelineAction: .setMode(mode: .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "testID")), + viewModel.process(timelineAction: .setMode(mode: .reply(eventID: "testID", replyDetails: .loaded(sender: .init(id: ""), eventID: "testID", eventContent: .message(.text(.init(body: "reply text")))), @@ -395,7 +395,7 @@ class ComposerToolbarViewModelTests: XCTestCase { await fulfillment(of: [expectation], timeout: 10) XCTAssertFalse(viewModel.context.composerFormattingEnabled) - XCTAssertEqual(viewModel.state.composerMode, .edit(originalItemId: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "testID")))) + XCTAssertEqual(viewModel.state.composerMode, .edit(originalEventOrTransactionID: .eventId(eventId: "testID"), source: .draftService)) XCTAssertEqual(viewModel.context.plainComposerText, NSAttributedString(string: "Hello world!")) } @@ -429,13 +429,13 @@ class ComposerToolbarViewModelTests: XCTestCase { await fulfillment(of: [draftExpectation], timeout: 10) XCTAssertFalse(viewModel.context.composerFormattingEnabled) // Testing the loading state first - XCTAssertEqual(viewModel.state.composerMode, .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: testEventID)), + XCTAssertEqual(viewModel.state.composerMode, .reply(eventID: testEventID, replyDetails: .loading(eventID: testEventID), isThread: false)) XCTAssertEqual(viewModel.context.plainComposerText, NSAttributedString(string: text)) await fulfillment(of: [loadReplyExpectation], timeout: 10) - XCTAssertEqual(viewModel.state.composerMode, .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: testEventID)), + XCTAssertEqual(viewModel.state.composerMode, .reply(eventID: testEventID, replyDetails: loadedReply, isThread: true)) } @@ -469,7 +469,7 @@ class ComposerToolbarViewModelTests: XCTestCase { await fulfillment(of: [draftExpectation], timeout: 10) XCTAssertFalse(viewModel.context.composerFormattingEnabled) // Testing the loading state first - XCTAssertEqual(viewModel.state.composerMode, .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: testEventID)), + XCTAssertEqual(viewModel.state.composerMode, .reply(eventID: testEventID, replyDetails: .loading(eventID: testEventID), isThread: false)) XCTAssertEqual(viewModel.context.plainComposerText, NSAttributedString(string: text)) @@ -483,7 +483,7 @@ class ComposerToolbarViewModelTests: XCTestCase { func testSaveVolatileDraftWhenEditing() { viewModel.context.composerFormattingEnabled = false viewModel.context.plainComposerText = .init(string: "Hello world!") - viewModel.process(timelineAction: .setMode(mode: .edit(originalItemId: .random))) + viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: UUID().uuidString), source: .timeline))) let draft = draftServiceMock.saveVolatileDraftReceivedDraft XCTAssertNotNil(draft) diff --git a/UnitTests/Sources/LoggingTests.swift b/UnitTests/Sources/LoggingTests.swift index 9b22fd2660..6887809a5a 100644 --- a/UnitTests/Sources/LoggingTests.swift +++ b/UnitTests/Sources/LoggingTests.swift @@ -116,7 +116,7 @@ class LoggingTests: XCTestCase { func validateTimelineContentIsRedacted() throws { // Given timeline items that contain text let textAttributedString = "TextAttributed" - let textMessage = TextRoomTimelineItem(id: .random, + let textMessage = TextRoomTimelineItem(id: .randomEvent, timestamp: "", isOutgoing: false, isEditable: false, @@ -125,7 +125,7 @@ class LoggingTests: XCTestCase { sender: .init(id: "sender"), content: .init(body: "TextString", formattedBody: AttributedString(textAttributedString))) let noticeAttributedString = "NoticeAttributed" - let noticeMessage = NoticeRoomTimelineItem(id: .random, + let noticeMessage = NoticeRoomTimelineItem(id: .randomEvent, timestamp: "", isOutgoing: false, isEditable: false, @@ -134,7 +134,7 @@ class LoggingTests: XCTestCase { sender: .init(id: "sender"), content: .init(body: "NoticeString", formattedBody: AttributedString(noticeAttributedString))) let emoteAttributedString = "EmoteAttributed" - let emoteMessage = EmoteRoomTimelineItem(id: .random, + let emoteMessage = EmoteRoomTimelineItem(id: .randomEvent, timestamp: "", isOutgoing: false, isEditable: false, @@ -142,7 +142,7 @@ class LoggingTests: XCTestCase { isThreaded: false, sender: .init(id: "sender"), content: .init(body: "EmoteString", formattedBody: AttributedString(emoteAttributedString))) - let imageMessage = ImageRoomTimelineItem(id: .init(uniqueID: "myimagemessage"), + let imageMessage = ImageRoomTimelineItem(id: .randomEvent, timestamp: "", isOutgoing: false, isEditable: false, @@ -153,7 +153,7 @@ class LoggingTests: XCTestCase { caption: "ImageString", source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/gif"), thumbnailSource: nil)) - let videoMessage = VideoRoomTimelineItem(id: .random, + let videoMessage = VideoRoomTimelineItem(id: .randomEvent, timestamp: "", isOutgoing: false, isEditable: false, @@ -165,7 +165,7 @@ class LoggingTests: XCTestCase { duration: 0, source: nil, thumbnailSource: nil)) - let fileMessage = FileRoomTimelineItem(id: .random, + let fileMessage = FileRoomTimelineItem(id: .randomEvent, timestamp: "", isOutgoing: false, isEditable: false, diff --git a/UnitTests/Sources/MediaPlayerProviderTests.swift b/UnitTests/Sources/MediaPlayerProviderTests.swift index a9fcca9a6a..92ef5997d5 100644 --- a/UnitTests/Sources/MediaPlayerProviderTests.swift +++ b/UnitTests/Sources/MediaPlayerProviderTests.swift @@ -60,7 +60,7 @@ class MediaPlayerProviderTests: XCTestCase { } func testPlayerStates() async throws { - let audioPlayerStateId = AudioPlayerStateIdentifier.timelineItemIdentifier(.random) + let audioPlayerStateId = AudioPlayerStateIdentifier.timelineItemIdentifier(.randomEvent) // By default, there should be no player state XCTAssertNil(mediaPlayerProvider.playerState(for: audioPlayerStateId)) @@ -76,7 +76,7 @@ class MediaPlayerProviderTests: XCTestCase { let audioPlayer = AudioPlayerMock() audioPlayer.actions = PassthroughSubject().eraseToAnyPublisher() - let audioPlayerStates = Array(repeating: AudioPlayerState(id: .timelineItemIdentifier(.random), title: "", duration: 0), count: 10) + let audioPlayerStates = Array(repeating: AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: "", duration: 0), count: 10) for audioPlayerState in audioPlayerStates { mediaPlayerProvider.register(audioPlayerState: audioPlayerState) audioPlayerState.attachAudioPlayer(audioPlayer) @@ -95,7 +95,7 @@ class MediaPlayerProviderTests: XCTestCase { let audioPlayer = AudioPlayerMock() audioPlayer.actions = PassthroughSubject().eraseToAnyPublisher() - let audioPlayerStates = Array(repeating: AudioPlayerState(id: .timelineItemIdentifier(.random), title: "", duration: 0), count: 10) + let audioPlayerStates = Array(repeating: AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: "", duration: 0), count: 10) for audioPlayerState in audioPlayerStates { mediaPlayerProvider.register(audioPlayerState: audioPlayerState) audioPlayerState.attachAudioPlayer(audioPlayer) diff --git a/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift b/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift index b0c8e2c96d..e96a8e2402 100644 --- a/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift +++ b/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift @@ -12,7 +12,7 @@ import XCTest @MainActor class MessageForwardingScreenViewModelTests: XCTestCase { - let forwardingItem = MessageForwardingItem(id: .init(uniqueID: "t1", eventOrTransactionID: .eventId(eventId: "t1")), + let forwardingItem = MessageForwardingItem(id: .event(uniqueID: "t1", eventOrTransactionID: .eventId(eventId: "t1")), roomID: "1", content: .init(noPointer: .init())) var viewModel: MessageForwardingScreenViewModelProtocol! diff --git a/UnitTests/Sources/ResolveVerifiedUserSendFailureScreenViewModelTests.swift b/UnitTests/Sources/ResolveVerifiedUserSendFailureScreenViewModelTests.swift index 91fc8e4279..f690d00367 100644 --- a/UnitTests/Sources/ResolveVerifiedUserSendFailureScreenViewModelTests.swift +++ b/UnitTests/Sources/ResolveVerifiedUserSendFailureScreenViewModelTests.swift @@ -61,7 +61,7 @@ class ResolveVerifiedUserSendFailureScreenViewModelTests: XCTestCase { private func makeViewModel(with failure: TimelineItemSendFailure.VerifiedUser) -> ResolveVerifiedUserSendFailureScreenViewModel { ResolveVerifiedUserSendFailureScreenViewModel(failure: failure, - itemID: .random, + itemID: .randomEvent, roomProxy: roomProxy, userIndicatorController: UserIndicatorControllerMock()) } diff --git a/UnitTests/Sources/TextBasedRoomTimelineTests.swift b/UnitTests/Sources/TextBasedRoomTimelineTests.swift index 775621fcbc..66a6d0e337 100644 --- a/UnitTests/Sources/TextBasedRoomTimelineTests.swift +++ b/UnitTests/Sources/TextBasedRoomTimelineTests.swift @@ -11,7 +11,7 @@ import XCTest final class TextBasedRoomTimelineTests: XCTestCase { func testTextRoomTimelineItemWhitespaceEnd() { let timestamp = "Now" - let timelineItem = TextRoomTimelineItem(id: .random, + let timelineItem = TextRoomTimelineItem(id: .randomEvent, timestamp: timestamp, isOutgoing: true, isEditable: true, @@ -24,7 +24,7 @@ final class TextBasedRoomTimelineTests: XCTestCase { func testTextRoomTimelineItemWhitespaceEndLonger() { let timestamp = "10:00 AM" - let timelineItem = TextRoomTimelineItem(id: .random, + let timelineItem = TextRoomTimelineItem(id: .randomEvent, timestamp: timestamp, isOutgoing: true, isEditable: true, @@ -37,7 +37,7 @@ final class TextBasedRoomTimelineTests: XCTestCase { func testTextRoomTimelineItemWhitespaceEndWithEdit() { let timestamp = "Now" - var timelineItem = TextRoomTimelineItem(id: .random, + var timelineItem = TextRoomTimelineItem(id: .randomEvent, timestamp: timestamp, isOutgoing: true, isEditable: true, @@ -52,7 +52,7 @@ final class TextBasedRoomTimelineTests: XCTestCase { func testTextRoomTimelineItemWhitespaceEndWithEditAndAlert() { let timestamp = "Now" - var timelineItem = TextRoomTimelineItem(id: .random, + var timelineItem = TextRoomTimelineItem(id: .randomEvent, timestamp: timestamp, isOutgoing: true, isEditable: true, diff --git a/UnitTests/Sources/TimelineViewModelTests.swift b/UnitTests/Sources/TimelineViewModelTests.swift index d1dcfc69b3..6625562c3f 100644 --- a/UnitTests/Sources/TimelineViewModelTests.swift +++ b/UnitTests/Sources/TimelineViewModelTests.swift @@ -423,7 +423,7 @@ class TimelineViewModelTests: XCTestCase { private extension TextRoomTimelineItem { init(text: String, sender: String, addReactions: Bool = false, addReadReceipts: [ReadReceipt] = []) { let reactions = addReactions ? [AggregatedReaction(accountOwnerID: "bob", key: "🦄", senders: [ReactionSender(id: sender, timestamp: Date())])] : [] - self.init(id: .random, + self.init(id: .randomEvent, timestamp: "10:47 am", isOutgoing: sender == "bob", isEditable: sender == "bob", @@ -437,13 +437,13 @@ private extension TextRoomTimelineItem { private extension SeparatorRoomTimelineItem { init(uniqueID: String) { - self.init(id: .init(uniqueID: uniqueID), text: "") + self.init(id: .virtual(uniqueID: uniqueID), text: "") } } private extension TextRoomTimelineItem { init(eventID: String) { - self.init(id: .init(uniqueID: UUID().uuidString, eventOrTransactionID: .eventId(eventId: eventID)), + self.init(id: .event(uniqueID: UUID().uuidString, eventOrTransactionID: .eventId(eventId: eventID)), timestamp: "", isOutgoing: false, isEditable: false, From 6078398ad36ed63e6cd5aeca4f4d54c502683634 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:50:24 +0100 Subject: [PATCH 060/114] Allow image uploads to be optimised to reduce bandwidth. (#3412) * Use typed throws for intermediate media upload preprocessing steps. * Remove unnecessary async/await usages. * Resize images when optimizedMediaUploads is enabled. * Reduce the JPEG quality. * Add tests for PNG and HEIC, fix mimetypes for these. * Add special handling for GIFs. * Test the files to make sure their mime types match the info. * Update the filename when converting formats. * Extend test timeout for video encoding. --- ElementX.xcodeproj/project.pbxproj | 8 + .../Media/MediaUploadingPreprocessor.swift | 336 ++++++++++-------- .../Resources/Media/test_animated_image.gif | 3 + .../Resources/Media/test_apple_image.heic | 3 + .../MediaUploadingPreprocessorTests.swift | 248 ++++++++++++- 5 files changed, 433 insertions(+), 165 deletions(-) create mode 100644 UnitTests/Resources/Media/test_animated_image.gif create mode 100644 UnitTests/Resources/Media/test_apple_image.heic diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index b1b8124cdd..67bd0831f0 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -278,6 +278,7 @@ 3DA57CA0D609A6B37CA1DC2F /* BugReportService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DC38E64A5ED3FDB201029A /* BugReportService.swift */; }; 3DAD62988F072607441CB7A5 /* PollFormScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F8664F1FB95AF3C4202478 /* PollFormScreenCoordinator.swift */; }; 3DAF325D8AE461F7CDB282BD /* StartChatScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6861FE915C7B5466E6962BBA /* StartChatScreen.swift */; }; + 3E23BB48F91485D893D0A429 /* test_animated_image.gif in Resources */ = {isa = PBXBuildFile; fileRef = 9C2BCB402FEB0FA1A54BEF4B /* test_animated_image.gif */; }; 3E7B65C2C97748D5D65AAA8B /* NotificationPermissionsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DB2FC2AA9A07EE792DF65CF /* NotificationPermissionsScreenModels.swift */; }; 3EC5A41F9FB7DD63A4DC6144 /* RoomChangeRolesScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14B1DE3E2D5D26732C49036 /* RoomChangeRolesScreenViewModel.swift */; }; 3EC698F80DDEEFA273857841 /* ArrayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 893777A4997BBDB68079D4F5 /* ArrayTests.swift */; }; @@ -722,6 +723,7 @@ 9DE801D278AC34737467F937 /* VoiceMessageMediaManagerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 889DEDD63C68ABDA8AD29812 /* VoiceMessageMediaManagerProtocol.swift */; }; 9E838A62918E47BC72D6640D /* UserIndicatorPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AB54B4F94686CCF0289B72F /* UserIndicatorPresenter.swift */; }; 9EBDC79CAC9B63A0D626E333 /* LegalInformationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB2CAA266B921D128C35710 /* LegalInformationScreenCoordinator.swift */; }; + 9EF9773DBE3F6497A25CE236 /* test_apple_image.heic in Resources */ = {isa = PBXBuildFile; fileRef = F6B676B4866F5B383DE819B2 /* test_apple_image.heic */; }; 9F11B9F347F9E2D236799FB3 /* ElementCallServiceConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 406C90AF8C3E98DF5D4E5430 /* ElementCallServiceConstants.swift */; }; 9F11E743EA01482E78A438B0 /* GlobalSearchScreenCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22DB19219E6CC4D002E15D48 /* GlobalSearchScreenCell.swift */; }; 9F19096BFA629C0AC282B1E4 /* CreateRoomScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CEB4634C0DD7779C4AB504 /* CreateRoomScreenUITests.swift */; }; @@ -1873,6 +1875,7 @@ 9B663BE498BB39EADC24025D /* SettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenModels.swift; sourceTree = ""; }; 9B67DF223EEB8DCAF178A1D4 /* AnalyticsPromptScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptScreenCoordinator.swift; sourceTree = ""; }; 9B7D8D3638864B7482E148CC /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; + 9C2BCB402FEB0FA1A54BEF4B /* test_animated_image.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = test_animated_image.gif; sourceTree = ""; }; 9C3ACC093F88FD9888518561 /* AuthenticationStartScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationStartScreenViewModel.swift; sourceTree = ""; }; 9C4048041C1A6B20CB97FD18 /* TestMeasurementParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestMeasurementParser.swift; sourceTree = ""; }; 9C5E81214D27A6B898FC397D /* ElementX.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ElementX.entitlements; sourceTree = ""; }; @@ -2274,6 +2277,7 @@ F5D8FEB1FED10E995CB002F7 /* TimelineBubbleLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineBubbleLayout.swift; sourceTree = ""; }; F5E23D8EE6CBACF32F1EC874 /* MediaPlayerProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayerProviderProtocol.swift; sourceTree = ""; }; F64A8582F65567AC38C2976A /* PollFormScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenViewModel.swift; sourceTree = ""; }; + F6B676B4866F5B383DE819B2 /* test_apple_image.heic */ = {isa = PBXFileReference; path = test_apple_image.heic; sourceTree = ""; }; F72EFC8C634469F9262659C7 /* NSItemProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSItemProvider.swift; sourceTree = ""; }; F733F135E6D67BBBEB76CC30 /* AppLockUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockUITests.swift; sourceTree = ""; }; F74532E01B317C56C1BE8FA8 /* RoomTimelineProviderMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProviderMock.swift; sourceTree = ""; }; @@ -4699,6 +4703,8 @@ 9A2AC7BE17C05CF7D2A22338 /* landscape_test_video.mov */, AF042B0FB2EE88977C91E330 /* portrait_test_image.jpg */, F2D513D2477B57F90E98EEC0 /* portrait_test_video.mp4 */, + 9C2BCB402FEB0FA1A54BEF4B /* test_animated_image.gif */, + F6B676B4866F5B383DE819B2 /* test_apple_image.heic */, D5E26C54362206BBDD096D83 /* test_audio.mp3 */, C733D11B421CFE3A657EF230 /* test_image.png */, 3FFDA99C98BE05F43A92343B /* test_pdf.pdf */, @@ -5874,6 +5880,8 @@ 858276B19C7C0AD4CA98EA78 /* portrait_test_image.jpg in Resources */, 6BB6944443C421C722ED1E7D /* portrait_test_video.mp4 in Resources */, 35E975CFDA60E05362A7CF79 /* target.yml in Resources */, + 3E23BB48F91485D893D0A429 /* test_animated_image.gif in Resources */, + 9EF9773DBE3F6497A25CE236 /* test_apple_image.heic in Resources */, 87CEDB8A0696F0D5AE2ABB28 /* test_audio.mp3 in Resources */, 21BF2B7CEDFE3CA67C5355AD /* test_image.png in Resources */, E77469C5CD7F7F58C0AC9752 /* test_pdf.pdf in Resources */, diff --git a/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift b/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift index ef88c3d797..0f967d8a5d 100644 --- a/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift +++ b/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift @@ -86,7 +86,8 @@ struct MediaUploadingPreprocessor { enum Constants { static let maximumThumbnailSize = CGSize(width: 800, height: 600) - static let thumbnailCompressionQuality = 0.8 + static let optimizedMaxPixelSize = 2048.0 + static let jpegCompressionQuality = 0.78 static let videoThumbnailTime = 5.0 // seconds } @@ -98,7 +99,7 @@ struct MediaUploadingPreprocessor { // Start by copying the file to a unique temporary location in order to avoid conflicts if processing it multiple times // All the other operations will be made relative to it let uniqueFolder = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString) - let newURL = uniqueFolder.appendingPathComponent(url.lastPathComponent) + var newURL = uniqueFolder.appendingPathComponent(url.lastPathComponent) do { try FileManager.default.createDirectory(at: uniqueFolder, withIntermediateDirectories: true) try FileManager.default.copyItem(at: url, to: newURL) @@ -109,17 +110,17 @@ struct MediaUploadingPreprocessor { // Process unknown types as plain files guard let type = UTType(filenameExtension: newURL.pathExtension), let mimeType = type.preferredMIMEType else { - return await processFile(at: newURL, mimeType: "application/octet-stream") + return processFile(at: newURL, mimeType: "application/octet-stream") } if type.conforms(to: .image) { - return await processImage(at: newURL, type: type, mimeType: mimeType) + return processImage(at: &newURL, type: type, mimeType: mimeType) } else if type.conforms(to: .movie) || type.conforms(to: .video) { return await processVideo(at: newURL) } else if type.conforms(to: .audio) { return await processAudio(at: newURL, mimeType: mimeType) } else { - return await processFile(at: newURL, mimeType: mimeType) + return processFile(at: newURL, mimeType: mimeType) } } @@ -131,34 +132,55 @@ struct MediaUploadingPreprocessor { /// - type: its UTType /// - mimeType: the mimeType extracted from the UTType /// - Returns: Returns a `MediaInfo.image` containing the URLs for the modified image and its thumbnail plus the corresponding `ImageInfo` - private func processImage(at url: URL, type: UTType, mimeType: String) async -> Result { - switch await stripLocationFromImage(at: url, type: type, mimeType: mimeType) { - case .success(let result): - switch await generateThumbnailForImage(at: url) { - case .success(let thumbnailResult): - let imageSize = (try? UInt64(FileManager.default.sizeForItem(at: result.url))) ?? 0 - let thumbnailSize = (try? UInt64(FileManager.default.sizeForItem(at: thumbnailResult.url))) ?? 0 - - let thumbnailInfo = ThumbnailInfo(height: UInt64(thumbnailResult.height), - width: UInt64(thumbnailResult.width), - mimetype: thumbnailResult.mimeType, - size: thumbnailSize) - - let imageInfo = ImageInfo(height: UInt64(result.height), - width: UInt64(result.width), - mimetype: result.mimeType, - size: imageSize, - thumbnailInfo: thumbnailInfo, - thumbnailSource: nil, - blurhash: thumbnailResult.blurhash) - - let mediaInfo = MediaInfo.image(imageURL: result.url, thumbnailURL: thumbnailResult.url, imageInfo: imageInfo) + private func processImage(at url: inout URL, type: UTType, mimeType: String) -> Result { + do { + try stripLocationFromImage(at: url, type: type) + + var mimeType = mimeType + if appSettings.optimizeMediaUploads, !type.conforms(to: .gif) { + let outputType = type.conforms(to: .png) ? UTType.png : .jpeg + mimeType = outputType.preferredMIMEType ?? "application/octet-stream" + try resizeImage(at: url, maxPixelSize: Constants.optimizedMaxPixelSize, destination: url, type: outputType) - return .success(mediaInfo) - case .failure(let error): - return .failure(.failedProcessingImage(error)) + if let preferredFilenameExtension = outputType.preferredFilenameExtension, + url.pathExtension != preferredFilenameExtension { + let convertedURL = url.deletingPathExtension().appendingPathExtension(preferredFilenameExtension) + do { + try FileManager.default.moveItem(at: url, to: convertedURL) + } catch { + return .failure(.failedResizingImage) + } + url = convertedURL + } } - case .failure(let error): + + let thumbnailResult = try generateThumbnailForImage(at: url) + + guard let imageSource = CGImageSourceCreateWithURL(url as NSURL, nil), + let imageSize = imageSource.size else { + return .failure(.failedProcessingImage(.failedStrippingLocationData)) + } + + let fileSize = (try? UInt64(FileManager.default.sizeForItem(at: url))) ?? 0 + let thumbnailFileSize = (try? UInt64(FileManager.default.sizeForItem(at: thumbnailResult.url))) ?? 0 + + let thumbnailInfo = ThumbnailInfo(height: UInt64(thumbnailResult.height), + width: UInt64(thumbnailResult.width), + mimetype: thumbnailResult.mimeType, + size: thumbnailFileSize) + + let imageInfo = ImageInfo(height: UInt64(imageSize.height), + width: UInt64(imageSize.width), + mimetype: mimeType, + size: fileSize, + thumbnailInfo: thumbnailInfo, + thumbnailSource: nil, + blurhash: thumbnailResult.blurhash) + + let mediaInfo = MediaInfo.image(imageURL: url, thumbnailURL: thumbnailResult.url, imageInfo: imageInfo) + + return .success(mediaInfo) + } catch { return .failure(.failedProcessingImage(error)) } } @@ -170,34 +192,31 @@ struct MediaUploadingPreprocessor { /// - mimeType: the mimeType extracted from the UTType /// - Returns: Returns a `MediaInfo.video` containing the URLs for the modified video and its thumbnail plus the corresponding `VideoInfo` private func processVideo(at url: URL) async -> Result { - switch await convertVideoToMP4(url) { - case .success(let result): - switch await generateThumbnailForVideoAt(result.url) { - case .success(let thumbnailResult): - let videoSize = (try? UInt64(FileManager.default.sizeForItem(at: result.url))) ?? 0 - let thumbnailSize = (try? UInt64(FileManager.default.sizeForItem(at: thumbnailResult.url))) ?? 0 - - let thumbnailInfo = ThumbnailInfo(height: UInt64(thumbnailResult.height), - width: UInt64(thumbnailResult.width), - mimetype: thumbnailResult.mimeType, - size: thumbnailSize) - - let videoInfo = VideoInfo(duration: result.duration, - height: UInt64(result.height), - width: UInt64(result.width), - mimetype: result.mimeType, - size: videoSize, - thumbnailInfo: thumbnailInfo, - thumbnailSource: nil, - blurhash: thumbnailResult.blurhash) - - let mediaInfo = MediaInfo.video(videoURL: result.url, thumbnailURL: thumbnailResult.url, videoInfo: videoInfo) - - return .success(mediaInfo) - case .failure(let error): - return .failure(.failedProcessingVideo(error)) - } - case .failure(let error): + do { + let result = try await convertVideoToMP4(url) + let thumbnailResult = try await generateThumbnailForVideoAt(result.url) + + let videoSize = (try? UInt64(FileManager.default.sizeForItem(at: result.url))) ?? 0 + let thumbnailSize = (try? UInt64(FileManager.default.sizeForItem(at: thumbnailResult.url))) ?? 0 + + let thumbnailInfo = ThumbnailInfo(height: UInt64(thumbnailResult.height), + width: UInt64(thumbnailResult.width), + mimetype: thumbnailResult.mimeType, + size: thumbnailSize) + + let videoInfo = VideoInfo(duration: result.duration, + height: UInt64(result.height), + width: UInt64(result.width), + mimetype: result.mimeType, + size: videoSize, + thumbnailInfo: thumbnailInfo, + thumbnailSource: nil, + blurhash: thumbnailResult.blurhash) + + let mediaInfo = MediaInfo.video(videoURL: result.url, thumbnailURL: thumbnailResult.url, videoInfo: videoInfo) + + return .success(mediaInfo) + } catch { return .failure(.failedProcessingVideo(error)) } } @@ -225,31 +244,30 @@ struct MediaUploadingPreprocessor { /// - type: its UTType /// - mimeType: the mimeType extracted from the UTType /// - Returns: Returns a `MediaInfo.file` containing the file URL plus the corresponding `FileInfo` - private func processFile(at url: URL, mimeType: String?) async -> Result { + private func processFile(at url: URL, mimeType: String?) -> Result { let fileSize = (try? UInt64(FileManager.default.sizeForItem(at: url))) ?? 0 let fileInfo = FileInfo(mimetype: mimeType, size: fileSize, thumbnailInfo: nil, thumbnailSource: nil) return .success(.file(fileURL: url, fileInfo: fileInfo)) } - // MARK: Images + // MARK: Image Helpers /// Removes the GPS dictionary from an image's metadata /// - Parameters: /// - url: the URL for the original image /// - type: its UTType /// - Returns: the URL for the modified image and its size as an `ImageProcessingResult` - private func stripLocationFromImage(at url: URL, type: UTType, mimeType: String) async -> Result { + private func stripLocationFromImage(at url: URL, type: UTType) throws(MediaUploadingPreprocessorError) { guard let originalData = NSData(contentsOf: url), - let originalImage = UIImage(data: originalData as Data), let imageSource = CGImageSourceCreateWithData(originalData, nil) else { - return .failure(.failedStrippingLocationData) + throw .failedStrippingLocationData } guard let originalMetadata = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil), (originalMetadata as NSDictionary).value(forKeyPath: "\(kCGImagePropertyGPSDictionary)") != nil else { - MXLog.info("No GPS metadata found. Returning original image") - return .success(.init(url: url, height: Double(originalImage.size.height), width: Double(originalImage.size.width), mimeType: mimeType, blurhash: nil)) + MXLog.info("No GPS metadata found. Nothing to do.") + return } let count = CGImageSourceGetCount(imageSource) @@ -257,110 +275,111 @@ struct MediaUploadingPreprocessor { let data = NSMutableData() guard let destination = CGImageDestinationCreateWithData(data as CFMutableData, type.identifier as CFString, count, nil) else { - return .failure(.failedStrippingLocationData) + throw .failedStrippingLocationData } CGImageDestinationAddImageFromSource(destination, imageSource, 0, metadataKeysToRemove as NSDictionary) CGImageDestinationFinalize(destination) do { try data.write(to: url) - return .success(.init(url: url, height: Double(originalImage.size.height), width: Double(originalImage.size.width), mimeType: mimeType, blurhash: nil)) } catch { - return .failure(.failedStrippingLocationData) + throw .failedStrippingLocationData } } /// Generates a thumbnail for an image /// - Parameter url: the original image URL /// - Returns: the URL for the resulting thumbnail and its sizing info as an `ImageProcessingResult` - private func generateThumbnailForImage(at url: URL) async -> Result { - switch await resizeImage(at: url, targetSize: Constants.maximumThumbnailSize) { - case .success(let thumbnail): - guard let data = thumbnail.jpegData(compressionQuality: Constants.thumbnailCompressionQuality) else { - return .failure(.failedGeneratingImageThumbnail(nil)) - } - - let blurhash = thumbnail.blurHash(numberOfComponents: (3, 3)) - - do { - let fileName = "thumbnail-\((url.lastPathComponent as NSString).deletingPathExtension).jpeg" - let thumbnailURL = url.deletingLastPathComponent().appendingPathComponent(fileName) - try data.write(to: thumbnailURL) - return .success(.init(url: thumbnailURL, height: thumbnail.size.height, width: thumbnail.size.width, mimeType: "image/jpeg", blurhash: blurhash)) - } catch { - return .failure(.failedGeneratingImageThumbnail(error)) - } - - case .failure(let error): - return .failure(.failedGeneratingImageThumbnail(error)) + private func generateThumbnailForImage(at url: URL) throws(MediaUploadingPreprocessorError) -> ImageProcessingInfo { + let thumbnailFileName = "thumbnail-\((url.lastPathComponent as NSString).deletingPathExtension).jpeg" + let thumbnailURL = url.deletingLastPathComponent().appendingPathComponent(thumbnailFileName) + let thumbnailMaxPixelSize = max(Constants.maximumThumbnailSize.height, Constants.maximumThumbnailSize.width) + + do { + try resizeImage(at: url, maxPixelSize: thumbnailMaxPixelSize, destination: thumbnailURL, type: .jpeg) + } catch { + throw .failedGeneratingImageThumbnail(error) + } + + guard let thumbnail = try? UIImage(contentsOf: thumbnailURL, cachePolicy: .useProtocolCachePolicy) else { + throw .failedGeneratingImageThumbnail(nil) } + let blurhash = thumbnail.blurHash(numberOfComponents: (3, 3)) + + return .init(url: thumbnailURL, height: thumbnail.size.height, width: thumbnail.size.width, mimeType: "image/jpeg", blurhash: blurhash) } - private func resizeImage(at url: URL, targetSize: CGSize) async -> Result { - let imageSource = CGImageSourceCreateWithURL(url as NSURL, nil) - guard let imageSource else { - return .failure(.failedResizingImage) + private func resizeImage(at url: URL, maxPixelSize: CGFloat, destination: URL, type: UTType) throws(MediaUploadingPreprocessorError) { + guard let imageSource = CGImageSourceCreateWithURL(url as NSURL, nil) else { + throw .failedResizingImage } - return await resizeImage(withSource: imageSource, targetSize: targetSize) + try resizeImage(withSource: imageSource, maxPixelSize: maxPixelSize, destination: destination, type: type) } /// Aspect ratio resizes an image so it fits in the given size. This is useful for resizing images without loading them directly into memory /// - Parameters: /// - imageSource: the original image `CGImageSource` - /// - targetSize: maximum resulting size + /// - maxPixelSize: maximum resulting size for the largest dimension of the image. /// - Returns: the resized image - private func resizeImage(withSource imageSource: CGImageSource, targetSize: CGSize) async -> Result { - let maximumSize = max(targetSize.height, targetSize.width) - + private func resizeImage(withSource imageSource: CGImageSource, maxPixelSize: CGFloat, destination destinationURL: URL, type: UTType) throws(MediaUploadingPreprocessorError) { let options: [NSString: Any] = [ // The maximum width and height in pixels of a thumbnail. - kCGImageSourceThumbnailMaxPixelSize: maximumSize, + kCGImageSourceThumbnailMaxPixelSize: maxPixelSize, kCGImageSourceCreateThumbnailFromImageAlways: true, // Should include kCGImageSourceCreateThumbnailWithTransform: true in the options dictionary. Otherwise, the image result will appear rotated when an image is taken from camera in the portrait orientation. kCGImageSourceCreateThumbnailWithTransform: true ] - guard let scaledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary) else { - return .failure(.failedResizingImage) + guard let scaledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as NSDictionary), + let destination = CGImageDestinationCreateWithURL(destinationURL as CFURL, type.identifier as CFString, 1, nil) else { + throw .failedResizingImage } - - return .success(UIImage(cgImage: scaledImage)) + let properties = [kCGImageDestinationLossyCompressionQuality: Constants.jpegCompressionQuality] + + CGImageDestinationAddImage(destination, scaledImage, properties as NSDictionary) + CGImageDestinationFinalize(destination) } - // MARK: Videos + // MARK: Video Helpers /// Generates a thumbnail for the video at the given URL /// - Parameter url: the video URL /// - Returns: the URL for the resulting thumbnail and its sizing info as an `ImageProcessingResult` - private func generateThumbnailForVideoAt(_ url: URL) async -> Result { + private func generateThumbnailForVideoAt(_ url: URL) async throws(MediaUploadingPreprocessorError) -> ImageProcessingInfo { let assetImageGenerator = AVAssetImageGenerator(asset: AVAsset(url: url)) assetImageGenerator.appliesPreferredTrackTransform = true assetImageGenerator.maximumSize = Constants.maximumThumbnailSize + // Avoid the first frames as on a lot of videos they're black. + // If the specified seconds are longer than the actual video a frame close to the end of the video will be used, at AVFoundation's discretion + let location = CMTime(seconds: Constants.videoThumbnailTime, preferredTimescale: 1) + + let cgImage: CGImage + do { + cgImage = try await assetImageGenerator.image(at: location).image + } catch { + throw .failedGeneratingVideoThumbnail(error) + } + + let thumbnail = UIImage(cgImage: cgImage) + + guard let data = thumbnail.jpegData(compressionQuality: Constants.jpegCompressionQuality) else { + throw .failedGeneratingVideoThumbnail(nil) + } + + let blurhash = thumbnail.blurHash(numberOfComponents: (3, 3)) + + let fileName = "\((url.lastPathComponent as NSString).deletingPathExtension).jpeg" + let thumbnailURL = url.deletingLastPathComponent().appendingPathComponent(fileName) + do { - // Avoid the first frames as on a lot of videos they're black. - // If the specified seconds are longer than the actual video a frame close to the end of the video will be used, at AVFoundation's discretion - let location = CMTime(seconds: Constants.videoThumbnailTime, preferredTimescale: 1) - let cgImage = try await assetImageGenerator.image(at: location).image - - let thumbnail = UIImage(cgImage: cgImage) - - guard let data = thumbnail.jpegData(compressionQuality: Constants.thumbnailCompressionQuality) else { - return .failure(.failedGeneratingVideoThumbnail(nil)) - } - - let blurhash = thumbnail.blurHash(numberOfComponents: (3, 3)) - - let fileName = "\((url.lastPathComponent as NSString).deletingPathExtension).jpeg" - let thumbnailURL = url.deletingLastPathComponent().appendingPathComponent(fileName) try data.write(to: thumbnailURL) - - return .success(.init(url: thumbnailURL, height: thumbnail.size.height, width: thumbnail.size.width, mimeType: "image/jpeg", blurhash: blurhash)) - } catch { - return .failure(.failedGeneratingVideoThumbnail(error)) + throw .failedGeneratingVideoThumbnail(error) } + + return .init(url: thumbnailURL, height: thumbnail.size.height, width: thumbnail.size.width, mimeType: "image/jpeg", blurhash: blurhash) } /// Converts the given video to an 1080p mp4 @@ -368,12 +387,12 @@ struct MediaUploadingPreprocessor { /// - url: the original video URL /// - targetFileSize: the maximum resulting file size. 90% of this will be used /// - Returns: the URL for the resulting video and its media info as a `VideoProcessingResult` - private func convertVideoToMP4(_ url: URL, targetFileSize: UInt = 0) async -> Result { + private func convertVideoToMP4(_ url: URL, targetFileSize: UInt = 0) async throws(MediaUploadingPreprocessorError) -> VideoProcessingInfo { let asset = AVURLAsset(url: url) let presetName = appSettings.optimizeMediaUploads ? AVAssetExportPreset640x480 : AVAssetExportPreset1920x1080 guard let exportSession = AVAssetExportSession(asset: asset, presetName: presetName) else { - return .failure(.failedConvertingVideo) + throw .failedConvertingVideo } // AVAssetExportSession will fail if the output URL already exists @@ -387,7 +406,7 @@ struct MediaUploadingPreprocessor { exportSession.outputFileType = AVFileType.mp4 guard exportSession.supportedFileTypes.contains(AVFileType.mp4) else { - return .failure(.failedConvertingVideo) + throw .failedConvertingVideo } if targetFileSize > 0 { @@ -397,33 +416,44 @@ struct MediaUploadingPreprocessor { await exportSession.export() - switch exportSession.status { - case .completed: - do { - // Delete the original - try? FileManager.default.removeItem(at: url) - // Strip the UUID from the new version - let newOutputURL = url.deletingLastPathComponent().appendingPathComponent("\(originalFilenameWithoutExtension).mp4") - try FileManager.default.moveItem(at: outputURL, to: newOutputURL) - - let newAsset = AVURLAsset(url: newOutputURL) - guard let track = try? await newAsset.loadTracks(withMediaType: .video).first, - let durationInSeconds = try? await newAsset.load(.duration).seconds, - let adjustedNaturalSize = try? await track.size else { - return .failure(.failedConvertingVideo) - } - - return .success(.init(url: newOutputURL, - height: adjustedNaturalSize.height, - width: adjustedNaturalSize.width, - duration: durationInSeconds, - mimeType: "video/mp4")) - } catch { - return .failure(.failedConvertingVideo) - } - default: - return .failure(.failedConvertingVideo) + guard exportSession.status == .completed else { + throw .failedConvertingVideo + } + + // Delete the original + try? FileManager.default.removeItem(at: url) + // Strip the UUID from the new version + let newOutputURL = url.deletingLastPathComponent().appendingPathComponent("\(originalFilenameWithoutExtension).mp4") + + do { try FileManager.default.moveItem(at: outputURL, to: newOutputURL) } catch { + throw .failedConvertingVideo + } + + let newAsset = AVURLAsset(url: newOutputURL) + guard let track = try? await newAsset.loadTracks(withMediaType: .video).first, + let durationInSeconds = try? await newAsset.load(.duration).seconds, + let adjustedNaturalSize = try? await track.size else { + throw .failedConvertingVideo + } + + return .init(url: newOutputURL, + height: adjustedNaturalSize.height, + width: adjustedNaturalSize.width, + duration: durationInSeconds, + mimeType: "video/mp4") + } +} + +// MARK: - Extensions + +private extension CGImageSource { + var size: CGSize? { + guard let properties = CGImageSourceCopyPropertiesAtIndex(self, 0, nil) as? [NSString: Any], + let width = properties[kCGImagePropertyPixelWidth] as? Int, + let height = properties[kCGImagePropertyPixelHeight] as? Int else { + return nil } + return CGSize(width: width, height: height) } } diff --git a/UnitTests/Resources/Media/test_animated_image.gif b/UnitTests/Resources/Media/test_animated_image.gif new file mode 100644 index 0000000000..c31ede46c1 --- /dev/null +++ b/UnitTests/Resources/Media/test_animated_image.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba429285ac3d7e29a5167bd7fab9a5b5721a9817afe824f61436520f6e6651ec +size 127823 diff --git a/UnitTests/Resources/Media/test_apple_image.heic b/UnitTests/Resources/Media/test_apple_image.heic new file mode 100644 index 0000000000..2b53713818 --- /dev/null +++ b/UnitTests/Resources/Media/test_apple_image.heic @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f110a5cd97e0e9b38258833274944e816f36379631eeb6883ddf6babb9bef37 +size 1827200 diff --git a/UnitTests/Sources/MediaUploadingPreprocessorTests.swift b/UnitTests/Sources/MediaUploadingPreprocessorTests.swift index 8f4e7c8443..7438c3a84c 100644 --- a/UnitTests/Sources/MediaUploadingPreprocessorTests.swift +++ b/UnitTests/Sources/MediaUploadingPreprocessorTests.swift @@ -5,6 +5,7 @@ // Please see LICENSE in the repository root for full details. // +import UniformTypeIdentifiers import XCTest @testable import ElementX @@ -45,6 +46,9 @@ final class MediaUploadingPreprocessorTests: XCTestCase { } func testLandscapeMovVideoProcessing() async { + // Allow double the default execution time as we encode the video twice now. + executionTimeAllowance = 120 + guard let url = Bundle(for: Self.self).url(forResource: "landscape_test_video.mov", withExtension: nil) else { XCTFail("Failed retrieving test asset") return @@ -58,6 +62,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase { // Check that the file name is preserved XCTAssertEqual(videoURL.lastPathComponent, "landscape_test_video.mp4") + XCTAssertEqual(videoURL.pathExtension, "mp4", "The file extension should match the container we use.") // Check that the thumbnail is generated correctly guard let thumbnailData = try? Data(contentsOf: thumbnailURL), @@ -79,7 +84,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase { XCTAssertNotNil(videoInfo.thumbnailInfo) XCTAssertEqual(videoInfo.thumbnailInfo?.mimetype, "image/jpeg") - XCTAssertEqual(videoInfo.thumbnailInfo?.size ?? 0, 34206, accuracy: 100) + XCTAssertEqual(videoInfo.thumbnailInfo?.size ?? 0, 33611, accuracy: 100) XCTAssertEqual(videoInfo.thumbnailInfo?.width, 800) XCTAssertEqual(videoInfo.thumbnailInfo?.height, 450) @@ -87,11 +92,13 @@ final class MediaUploadingPreprocessorTests: XCTestCase { appSettings.optimizeMediaUploads = true guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url), - case let .video(_, _, optimizedVideoInfo) = optimizedResult else { + case let .video(optimizedVideoURL, _, optimizedVideoInfo) = optimizedResult else { XCTFail("Failed processing asset") return } + XCTAssertEqual(optimizedVideoURL.pathExtension, "mp4", "The file extension should match the container we use.") + // Check optimised video info XCTAssertEqual(optimizedVideoInfo.mimetype, "video/mp4") XCTAssertEqual(optimizedVideoInfo.blurhash, "K32PJbx^I7jYaebHMvV?o$") @@ -102,6 +109,9 @@ final class MediaUploadingPreprocessorTests: XCTestCase { } func testPortraitMp4VideoProcessing() async { + // Allow double the default execution time as we encode the video twice now. + executionTimeAllowance = 120 + guard let url = Bundle(for: Self.self).url(forResource: "portrait_test_video.mp4", withExtension: nil) else { XCTFail("Failed retrieving test asset") return @@ -115,6 +125,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase { // Check that the file name is preserved XCTAssertEqual(videoURL.lastPathComponent, "portrait_test_video.mp4") + XCTAssertEqual(videoURL.pathExtension, "mp4", "The file extension should match the container we use.") // Check that the thumbnail is generated correctly guard let thumbnailData = try? Data(contentsOf: thumbnailURL), @@ -136,7 +147,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase { XCTAssertNotNil(videoInfo.thumbnailInfo) XCTAssertEqual(videoInfo.thumbnailInfo?.mimetype, "image/jpeg") - XCTAssertEqual(videoInfo.thumbnailInfo?.size ?? 0, 83220, accuracy: 100) + XCTAssertEqual(videoInfo.thumbnailInfo?.size ?? 0, 81515, accuracy: 100) XCTAssertEqual(videoInfo.thumbnailInfo?.width, 337) XCTAssertEqual(videoInfo.thumbnailInfo?.height, 600) @@ -144,11 +155,13 @@ final class MediaUploadingPreprocessorTests: XCTestCase { appSettings.optimizeMediaUploads = true guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url), - case let .video(_, _, optimizedVideoInfo) = optimizedResult else { + case let .video(optimizedVideoURL, _, optimizedVideoInfo) = optimizedResult else { XCTFail("Failed processing asset") return } + XCTAssertEqual(optimizedVideoURL.pathExtension, "mp4", "The file extension should match the container we use.") + // Check optimised video info XCTAssertEqual(optimizedVideoInfo.mimetype, "video/mp4") XCTAssertEqual(optimizedVideoInfo.blurhash, "K7BDNJD*0L%#sl_2~C9ZE1") @@ -181,9 +194,27 @@ final class MediaUploadingPreprocessorTests: XCTestCase { XCTAssertNotNil(imageInfo.thumbnailInfo) XCTAssertEqual(imageInfo.thumbnailInfo?.mimetype, "image/jpeg") - XCTAssertEqual(imageInfo.thumbnailInfo?.size ?? 0, 89553, accuracy: 100) + XCTAssertEqual(imageInfo.thumbnailInfo?.size ?? 0, 87733, accuracy: 100) XCTAssertEqual(imageInfo.thumbnailInfo?.width, 800) XCTAssertEqual(imageInfo.thumbnailInfo?.height, 344) + + // Repeat with optimised media setting + appSettings.optimizeMediaUploads = true + + guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url), + case let .image(optimizedImageURL, thumbnailURL, optimizedImageInfo) = optimizedResult else { + XCTFail("Failed processing asset") + return + } + + compare(originalImageAt: url, toConvertedImageAt: optimizedImageURL, withThumbnailAt: thumbnailURL) + + // Check optimised image info + XCTAssertEqual(optimizedImageInfo.mimetype, "image/jpeg") + XCTAssertEqual(optimizedImageInfo.blurhash, "K%I#.NofkC_4ayaxxujsWB") + XCTAssertEqual(optimizedImageInfo.size ?? 0, 524_226, accuracy: 100) + XCTAssertEqual(optimizedImageInfo.width, 2048) + XCTAssertEqual(optimizedImageInfo.height, 879) } func testPortraitImageProcessing() async { @@ -202,16 +233,193 @@ final class MediaUploadingPreprocessorTests: XCTestCase { // Check resulting image info XCTAssertEqual(imageInfo.mimetype, "image/jpeg") - XCTAssertEqual(imageInfo.blurhash, "KdE:ets+RP^-n*RP%OWAV@") + XCTAssertEqual(imageInfo.blurhash, "KdE|0Ls+RP^-n*RP%OWAV@") XCTAssertEqual(imageInfo.size ?? 0, 4_414_666, accuracy: 100) XCTAssertEqual(imageInfo.width, 3024) XCTAssertEqual(imageInfo.height, 4032) XCTAssertNotNil(imageInfo.thumbnailInfo) XCTAssertEqual(imageInfo.thumbnailInfo?.mimetype, "image/jpeg") - XCTAssertEqual(imageInfo.thumbnailInfo?.size ?? 0, 264_500, accuracy: 100) + XCTAssertEqual(imageInfo.thumbnailInfo?.size ?? 0, 258_914, accuracy: 100) + XCTAssertEqual(imageInfo.thumbnailInfo?.width, 600) + XCTAssertEqual(imageInfo.thumbnailInfo?.height, 800) + + // Repeat with optimised media setting + appSettings.optimizeMediaUploads = true + + guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url), + case let .image(optimizedImageURL, thumbnailURL, optimizedImageInfo) = optimizedResult else { + XCTFail("Failed processing asset") + return + } + + compare(originalImageAt: url, toConvertedImageAt: optimizedImageURL, withThumbnailAt: thumbnailURL) + + // Check optimised image info + XCTAssertEqual(optimizedImageInfo.mimetype, "image/jpeg") + XCTAssertEqual(optimizedImageInfo.blurhash, "KdE|0Ls+RP^-n*RP%OWAV@") + XCTAssertEqual(optimizedImageInfo.size ?? 0, 1_462_937, accuracy: 100) + XCTAssertEqual(optimizedImageInfo.width, 1536) + XCTAssertEqual(optimizedImageInfo.height, 2048) + } + + func testPNGImageProcessing() async { + guard let url = Bundle(for: Self.self).url(forResource: "test_image.png", withExtension: nil) else { + XCTFail("Failed retrieving test asset") + return + } + + guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url), + case let .image(convertedImageURL, _, imageInfo) = result else { + XCTFail("Failed processing asset") + return + } + + // Make sure the output file matches the image info. + XCTAssertEqual(mimeType(from: convertedImageURL), "image/png", "PNGs should always be sent as PNG to preserve the alpha channel.") + XCTAssertEqual(convertedImageURL.pathExtension, "png", "The file extension should match the MIME type.") + + // Check resulting image info + XCTAssertEqual(imageInfo.mimetype, "image/png") + XCTAssertEqual(imageInfo.blurhash, "K0TSUA~qfQ~qj[fQfQfQfQ") + XCTAssertEqual(imageInfo.size ?? 0, 4868, accuracy: 100) + XCTAssertEqual(imageInfo.width, 240) + XCTAssertEqual(imageInfo.height, 240) + + XCTAssertNotNil(imageInfo.thumbnailInfo) + XCTAssertEqual(imageInfo.thumbnailInfo?.mimetype, "image/jpeg") + XCTAssertEqual(imageInfo.thumbnailInfo?.size ?? 0, 1725, accuracy: 100) + XCTAssertEqual(imageInfo.thumbnailInfo?.width, 240) + XCTAssertEqual(imageInfo.thumbnailInfo?.height, 240) + + // Repeat with optimised media setting + appSettings.optimizeMediaUploads = true + + guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url), + case let .image(optimizedImageURL, _, optimizedImageInfo) = optimizedResult else { + XCTFail("Failed processing asset") + return + } + + // Make sure the output file matches the image info. + XCTAssertEqual(mimeType(from: optimizedImageURL), "image/png", "PNGs should always be sent as PNG to preserve the alpha channel.") + XCTAssertEqual(optimizedImageURL.pathExtension, "png", "The file extension should match the MIME type.") + + // Check optimised image info + XCTAssertEqual(optimizedImageInfo.mimetype, "image/png") + XCTAssertEqual(optimizedImageInfo.blurhash, "K0TSUA~qfQ~qj[fQfQfQfQ") + XCTAssertEqual(optimizedImageInfo.size ?? 0, 8199, accuracy: 100) + // Assert that resizing didn't upscale to the maxPixelSize. + XCTAssertEqual(optimizedImageInfo.width, 240) + XCTAssertEqual(optimizedImageInfo.height, 240) + } + + func testHEICImageProcessing() async { + guard let url = Bundle(for: Self.self).url(forResource: "test_apple_image.heic", withExtension: nil) else { + XCTFail("Failed retrieving test asset") + return + } + + guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url), + case let .image(convertedImageURL, thumbnailURL, imageInfo) = result else { + XCTFail("Failed processing asset") + return + } + + compare(originalImageAt: url, toConvertedImageAt: convertedImageURL, withThumbnailAt: thumbnailURL) + + // Make sure the output file matches the image info. + XCTAssertEqual(mimeType(from: convertedImageURL), "image/heic", "Unoptimised HEICs should always be sent as is.") + XCTAssertEqual(convertedImageURL.pathExtension, "heic", "The file extension should match the MIME type.") + + // Check resulting image info + XCTAssertEqual(imageInfo.mimetype, "image/heic") + XCTAssertEqual(imageInfo.blurhash, "KGD]3ns:T00$kWxFXmt6xv") + XCTAssertEqual(imageInfo.size ?? 0, 1_857_833, accuracy: 100) + XCTAssertEqual(imageInfo.width, 3024) + XCTAssertEqual(imageInfo.height, 4032) + + XCTAssertNotNil(imageInfo.thumbnailInfo) + XCTAssertEqual(imageInfo.thumbnailInfo?.mimetype, "image/jpeg") + XCTAssertEqual(imageInfo.thumbnailInfo?.size ?? 0, 218_108, accuracy: 100) XCTAssertEqual(imageInfo.thumbnailInfo?.width, 600) XCTAssertEqual(imageInfo.thumbnailInfo?.height, 800) + + // Repeat with optimised media setting + appSettings.optimizeMediaUploads = true + + guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url), + case let .image(optimizedImageURL, thumbnailURL, optimizedImageInfo) = optimizedResult else { + XCTFail("Failed processing asset") + return + } + + compare(originalImageAt: url, toConvertedImageAt: optimizedImageURL, withThumbnailAt: thumbnailURL) + + // Make sure the output file matches the image info. + XCTAssertEqual(mimeType(from: optimizedImageURL), "image/jpeg", "Optimised HEICs should always be converted to JPEG for compatibility.") + XCTAssertEqual(optimizedImageURL.pathExtension, "jpeg", "The file extension should match the MIME type.") + + // Check optimised image info + XCTAssertEqual(optimizedImageInfo.mimetype, "image/jpeg") + XCTAssertEqual(optimizedImageInfo.blurhash, "KGD]3ns:T00#kWxFb^s:xv") + XCTAssertEqual(optimizedImageInfo.size ?? 0, 1_049_393, accuracy: 100) + XCTAssertEqual(optimizedImageInfo.width, 1536) + XCTAssertEqual(optimizedImageInfo.height, 2048) + } + + func testGIFImageProcessing() async { + guard let url = Bundle(for: Self.self).url(forResource: "test_animated_image.gif", withExtension: nil) else { + XCTFail("Failed retrieving test asset") + return + } + guard let originalSize = try? FileManager.default.sizeForItem(at: url), originalSize > 0 else { + XCTFail("Failed fetching test asset's original size") + return + } + + guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url), + case let .image(convertedImageURL, _, imageInfo) = result else { + XCTFail("Failed processing asset") + return + } + + // Make sure the output file matches the image info. + XCTAssertEqual(mimeType(from: convertedImageURL), "image/gif", "GIFs should always be sent as GIF to preserve the animation.") + XCTAssertEqual(convertedImageURL.pathExtension, "gif", "The file extension should match the MIME type.") + + // Check resulting image info + XCTAssertEqual(imageInfo.mimetype, "image/gif") + XCTAssertEqual(imageInfo.blurhash, "K7SY{qs;%NxuRjof~qozIU") + XCTAssertEqual(imageInfo.size ?? 0, UInt64(originalSize), accuracy: 100) + XCTAssertEqual(imageInfo.width, 490) + XCTAssertEqual(imageInfo.height, 498) + + XCTAssertNotNil(imageInfo.thumbnailInfo) + XCTAssertEqual(imageInfo.thumbnailInfo?.mimetype, "image/jpeg") + XCTAssertEqual(imageInfo.thumbnailInfo?.size ?? 0, 29511, accuracy: 100) + XCTAssertEqual(imageInfo.thumbnailInfo?.width, 490) + XCTAssertEqual(imageInfo.thumbnailInfo?.height, 498) + + // Repeat with optimised media setting + appSettings.optimizeMediaUploads = true + + guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url), + case let .image(optimizedImageURL, _, optimizedImageInfo) = optimizedResult else { + XCTFail("Failed processing asset") + return + } + + // Make sure the output file matches the image info. + XCTAssertEqual(mimeType(from: optimizedImageURL), "image/gif", "GIFs should always be sent as GIF to preserve the animation.") + XCTAssertEqual(optimizedImageURL.pathExtension, "gif", "The file extension should match the MIME type.") + + // Ensure optimised image is still the same as the original image. + XCTAssertEqual(optimizedImageInfo.mimetype, "image/gif") + XCTAssertEqual(optimizedImageInfo.blurhash, "K7SY{qs;%NxuRjof~qozIU") + XCTAssertEqual(optimizedImageInfo.size ?? 0, UInt64(originalSize), accuracy: 100) + XCTAssertEqual(optimizedImageInfo.width, 490) + XCTAssertEqual(optimizedImageInfo.height, 498) } // MARK: - Private @@ -224,11 +432,16 @@ final class MediaUploadingPreprocessorTests: XCTestCase { fatalError() } - // Check that the file name is preserved - XCTAssertEqual(originalImageURL.lastPathComponent, convertedImageURL.lastPathComponent) - - // Check that new image is the same size as the original one - XCTAssertEqual(originalImage.size, convertedImage.size) + if appSettings.optimizeMediaUploads { + // Check that new image has been scaled within the requirements for an optimised image + XCTAssert(convertedImage.size.width <= MediaUploadingPreprocessor.Constants.optimizedMaxPixelSize) + XCTAssert(convertedImage.size.height <= MediaUploadingPreprocessor.Constants.optimizedMaxPixelSize) + } else { + // Check that the file name is preserved + XCTAssertEqual(originalImageURL.lastPathComponent, convertedImageURL.lastPathComponent) + // Check that new image is the same size as the original one + XCTAssertEqual(originalImage.size, convertedImage.size) + } // Check that the GPS data has been stripped let originalMetadata = metadata(from: originalImageData) @@ -269,4 +482,15 @@ final class MediaUploadingPreprocessorTests: XCTestCase { return convertedMetadata } + + private func mimeType(from url: URL) -> String? { + guard let imageSource = CGImageSourceCreateWithURL(url as NSURL, nil), + let typeIdentifier = CGImageSourceGetType(imageSource), + let type = UTType(typeIdentifier as String), + let mimeType = type.preferredMIMEType else { + XCTFail("Failed to get mimetype from URL.") + return nil + } + return mimeType + } } From 5a89219f9a146f936f52a5e1e75db29a6f196e46 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:51:29 +0100 Subject: [PATCH 061/114] Fix the pinned identity banner to always show the user ID regardless of ambiguity. (#3415) --- .../en.lproj/Localizable.strings | 2 ++ ElementX/Sources/Generated/Strings.swift | 8 +++++ .../View/RoomScreenFooterView.swift | 30 +++++++++++++++---- ...test_roomScreenFooterView-iPad-en-GB.1.png | 3 -- ...FooterView-iPad-en-GB.With-displayname.png | 3 ++ ...terView-iPad-en-GB.Without-displayname.png | 3 ++ ...est_roomScreenFooterView-iPad-pseudo.1.png | 3 -- ...ooterView-iPad-pseudo.With-displayname.png | 3 ++ ...erView-iPad-pseudo.Without-displayname.png | 3 ++ ...roomScreenFooterView-iPhone-16-en-GB.1.png | 3 -- ...rView-iPhone-16-en-GB.With-displayname.png | 3 ++ ...ew-iPhone-16-en-GB.Without-displayname.png | 3 ++ ...oomScreenFooterView-iPhone-16-pseudo.1.png | 3 -- ...View-iPhone-16-pseudo.With-displayname.png | 3 ++ ...w-iPhone-16-pseudo.Without-displayname.png | 3 ++ 15 files changed, 59 insertions(+), 17 deletions(-) delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.With-displayname.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.Without-displayname.png delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.With-displayname.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.Without-displayname.png delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.With-displayname.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.Without-displayname.png delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.With-displayname.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.Without-displayname.png diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index b4aadb523e..fea01d5a7a 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -241,6 +241,8 @@ "confirm_recovery_key_banner_title" = "Enter your recovery key"; "crash_detection_dialog_content" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "In order to let the application use the camera, please grant the permission in the system settings."; "dialog_permission_generic" = "Please grant the permission in the system settings."; "dialog_permission_location_description_ios" = "Grant access in Settings -> Location."; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index ec9b1c81e2..a14b6da1e7 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -524,6 +524,14 @@ internal enum L10n { internal static func cryptoIdentityChangePinViolation(_ p1: Any, _ p2: Any) -> String { return L10n.tr("Localizable", "crypto_identity_change_pin_violation", String(describing: p1), String(describing: p2)) } + /// %1$@’s %2$@ identity appears to have changed. %3$@ + internal static func cryptoIdentityChangePinViolationNew(_ p1: Any, _ p2: Any, _ p3: Any) -> String { + return L10n.tr("Localizable", "crypto_identity_change_pin_violation_new", String(describing: p1), String(describing: p2), String(describing: p3)) + } + /// (%1$@) + internal static func cryptoIdentityChangePinViolationNewUserId(_ p1: Any) -> String { + return L10n.tr("Localizable", "crypto_identity_change_pin_violation_new_user_id", String(describing: p1)) + } /// In order to let the application use the camera, please grant the permission in the system settings. internal static var dialogPermissionCamera: String { return L10n.tr("Localizable", "dialog_permission_camera") } /// Please grant the permission in the system settings. diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomScreenFooterView.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomScreenFooterView.swift index ec9e7df87f..3be6e50242 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/RoomScreenFooterView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomScreenFooterView.swift @@ -43,7 +43,8 @@ struct RoomScreenFooterView: View { avatarSize: .user(on: .timeline), mediaProvider: mediaProvider) - Text(pinViolationDescriptionWithLearnMoreLink(displayName: member.disambiguatedDisplayName ?? member.userID, + Text(pinViolationDescriptionWithLearnMoreLink(displayName: member.displayName, + userID: member.userID, url: learnMoreURL)) .font(.compound.bodyMD) .foregroundColor(.compound.textPrimary) @@ -59,20 +60,39 @@ struct RoomScreenFooterView: View { .padding(.bottom, 8) } - private func pinViolationDescriptionWithLearnMoreLink(displayName: String, url: URL) -> AttributedString { + private func pinViolationDescriptionWithLearnMoreLink(displayName: String?, userID: String, url: URL) -> AttributedString { + let userIDPlaceholder = "{mxid}" let linkPlaceholder = "{link}" - var description = AttributedString(L10n.cryptoIdentityChangePinViolation(displayName, linkPlaceholder)) + let displayName = displayName ?? fallbackDisplayName(userID) + var description = AttributedString(L10n.cryptoIdentityChangePinViolationNew(displayName, userIDPlaceholder, linkPlaceholder)) + + var userIDString = AttributedString(L10n.cryptoIdentityChangePinViolationNewUserId(userID)) + userIDString.bold() + description.replace(userIDPlaceholder, with: userIDString) + var linkString = AttributedString(L10n.actionLearnMore) linkString.link = url linkString.bold() description.replace(linkPlaceholder, with: linkString) return description } + + private func fallbackDisplayName(_ userID: String) -> String { + guard let localpart = userID.components(separatedBy: ":").first else { return userID } + return String(localpart.trimmingPrefix("@")) + } } struct RoomScreenFooterView_Previews: PreviewProvider, TestablePreview { + static let bobDetails: RoomScreenFooterViewDetails = .pinViolation(member: RoomMemberProxyMock.mockBob, + learnMoreURL: "https://element.io/") + static let noNameDetails: RoomScreenFooterViewDetails = .pinViolation(member: RoomMemberProxyMock.mockNoName, + learnMoreURL: "https://element.io/") + static var previews: some View { - RoomScreenFooterView(details: .pinViolation(member: RoomMemberProxyMock.mockBob, learnMoreURL: "https://element.io/"), - mediaProvider: MediaProviderMock(configuration: .init())) { _ in } + RoomScreenFooterView(details: bobDetails, mediaProvider: MediaProviderMock(configuration: .init())) { _ in } + .previewDisplayName("With displayname") + RoomScreenFooterView(details: noNameDetails, mediaProvider: MediaProviderMock(configuration: .init())) { _ in } + .previewDisplayName("Without displayname") } } diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.1.png deleted file mode 100644 index 43ba189302..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6ff5ac1cb8df1556b0d4ba1f119e65a182cb3ece6003f58db8a6129ef701dd94 -size 156835 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.With-displayname.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.With-displayname.png new file mode 100644 index 0000000000..a7e3c58989 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.With-displayname.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e01dbaab1c59563feea0b36e1859fcf9171f658eb1c03ec4a5bcc7442e399694 +size 156987 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.Without-displayname.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.Without-displayname.png new file mode 100644 index 0000000000..4e0c5e8d4b --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-en-GB.Without-displayname.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7515e499500d53851cee3922b03ae96fdfb02a9371ebc19669d3f1788bca1975 +size 160605 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.1.png deleted file mode 100644 index 8e484ff041..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b9693534082a1948f05d6f50c7e0cb8db768fdf6fe01189af4df64638edaae37 -size 166386 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.With-displayname.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.With-displayname.png new file mode 100644 index 0000000000..98acbb220a --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.With-displayname.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71dbd25ef19181d442d33c708f8bf04cbf710791bd5fca8371ee382855204b55 +size 169974 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.Without-displayname.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.Without-displayname.png new file mode 100644 index 0000000000..9a1a56a4fb --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPad-pseudo.Without-displayname.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d3b822afa220fedd562c956da594a019c0bd6391efd522e79e88adcebbebe36 +size 177460 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.1.png deleted file mode 100644 index 2019d816bd..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b02b43b978488a8e7b81d2767862a9a5289df638c502570557ef5f5e7a8dd4cc -size 77863 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.With-displayname.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.With-displayname.png new file mode 100644 index 0000000000..f074a714af --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.With-displayname.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:266b6f2530685c8be01e6c9f64139596330c52d6b3d268d46c94ab98ae136a46 +size 78221 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.Without-displayname.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.Without-displayname.png new file mode 100644 index 0000000000..1abb931ed2 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-en-GB.Without-displayname.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:692b1f60a6df116b1130d839928600765ecf68ca5f825c6bb231533aeb01c746 +size 86160 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.1.png deleted file mode 100644 index 658879046e..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c7ab7912c6041b90042ed19c52ae21ee6ea4e88f5592b692607e7f8eb01adc40 -size 99897 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.With-displayname.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.With-displayname.png new file mode 100644 index 0000000000..3ebc303e51 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.With-displayname.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:92c7085936fb313fb8bbf399acd1f83582ca940433119bd2ba41ea9351517ee1 +size 102769 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.Without-displayname.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.Without-displayname.png new file mode 100644 index 0000000000..d7bf650a92 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreenFooterView-iPhone-16-pseudo.Without-displayname.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:24524c9052a7bcc9b04b764c714680bcbcb87ef3e583cd12e8450e2b0db69829 +size 107246 From 0651314e3e7d299ded7b336268e0488189fc04b4 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 16 Oct 2024 19:08:34 +0300 Subject: [PATCH 062/114] Adopt various rust side Timeline API additions (#3423) * Adopt new reaction toggling API introduced in matrix-org/matrix-rust-sdk/pull/4127 * Adopt the changes introduced in matrix-org/matrix-rust-sdk/pull/4111: use the new `TimelineUniqueId` type instead of `String` for unique timeline identifiers. * Bump the RustSDK to v1.0.58. * Fix unit tests --- ElementX.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- .../RoomFlowCoordinator.swift | 6 +- .../Mocks/Generated/GeneratedMocks.swift | 28 ++-- .../Mocks/Generated/SDKGeneratedMocks.swift | 134 +++++++++--------- .../Mocks/RoomTimelineProviderMock.swift | 2 +- .../Sources/Mocks/SDK/ClientSDKMock.swift | 2 +- .../Timeline/TimelineInteractionHandler.swift | 14 +- .../Screens/Timeline/TimelineModels.swift | 5 +- .../TimelineTableViewController.swift | 19 +-- .../Screens/Timeline/TimelineViewModel.swift | 10 +- .../Style/TimelineItemBubbledStylerView.swift | 2 +- .../Timeline/View/Style/TimelineStyler.swift | 4 +- .../CollapsibleRoomTimelineView.swift | 4 +- .../ReadMarkerRoomTimelineView.swift | 4 +- .../SeparatorRoomTimelineView.swift | 2 +- .../AuthenticationService.swift | 4 +- .../RoomSummary/RoomSummaryProvider.swift | 2 +- .../Fixtures/RoomTimelineItemFixtures.swift | 20 +-- .../MockRoomTimelineController.swift | 4 +- .../RoomTimelineController.swift | 10 +- .../RoomTimelineControllerProtocol.swift | 4 +- .../Timeline/TimelineItemIdentifier.swift | 10 +- .../Services/Timeline/TimelineItemProxy.swift | 8 +- .../PaginationIndicatorRoomTimelineItem.swift | 2 +- .../TimelineStartRoomTimelineItem.swift | 2 +- .../RoomTimelineItemViewState.swift | 3 +- .../Services/Timeline/TimelineProxy.swift | 23 ++- .../Timeline/TimelineProxyProtocol.swift | 4 +- UnitTests/Sources/LoggingTests.swift | 12 +- ...essageForwardingScreenViewModelTests.swift | 2 +- .../Sources/RoomScreenViewModelTests.swift | 16 +-- .../Sources/TimelineItemFactoryTests.swift | 2 +- .../Sources/TimelineViewModelTests.swift | 13 +- project.yml | 2 +- 35 files changed, 201 insertions(+), 184 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 67bd0831f0..56e4832517 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7806,7 +7806,7 @@ repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.0.57; + version = 1.0.58; }; }; 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index efa35cd9e8..0c07855fa0 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/matrix-rust-components-swift", "state" : { - "revision" : "5f01589d44aa7045dc1ad87f2583f6083295d3eb", - "version" : "1.0.57" + "revision" : "753c5381ce88b3549cbd8ed9b839109ff143ecdd", + "version" : "1.0.58" } }, { diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index fb48a3cd71..1c10cdbc56 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -933,7 +933,11 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { MXLog.debug("Selected \(emoji) for \(itemID)") navigationStackCoordinator.setSheetCoordinator(nil) Task { - await self.timelineController?.toggleReaction(emoji, to: itemID) + guard case let .event(_, eventOrTransactionID) = itemID else { + fatalError() + } + + await self.timelineController?.toggleReaction(emoji, to: eventOrTransactionID) } case .dismiss: navigationStackCoordinator.setSheetCoordinator(nil) diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index e3c0bace6a..1b472cd45a 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -14057,8 +14057,8 @@ class TimelineProxyMock: TimelineProxyProtocol { var redactReasonCalled: Bool { return redactReasonCallsCount > 0 } - var redactReasonReceivedArguments: (timelineItemID: TimelineItemIdentifier, reason: String?)? - var redactReasonReceivedInvocations: [(timelineItemID: TimelineItemIdentifier, reason: String?)] = [] + var redactReasonReceivedArguments: (eventOrTransactionID: EventOrTransactionId, reason: String?)? + var redactReasonReceivedInvocations: [(eventOrTransactionID: EventOrTransactionId, reason: String?)] = [] var redactReasonUnderlyingReturnValue: Result! var redactReasonReturnValue: Result! { @@ -14084,16 +14084,16 @@ class TimelineProxyMock: TimelineProxyProtocol { } } } - var redactReasonClosure: ((TimelineItemIdentifier, String?) async -> Result)? + var redactReasonClosure: ((EventOrTransactionId, String?) async -> Result)? - func redact(_ timelineItemID: TimelineItemIdentifier, reason: String?) async -> Result { + func redact(_ eventOrTransactionID: EventOrTransactionId, reason: String?) async -> Result { redactReasonCallsCount += 1 - redactReasonReceivedArguments = (timelineItemID: timelineItemID, reason: reason) + redactReasonReceivedArguments = (eventOrTransactionID: eventOrTransactionID, reason: reason) DispatchQueue.main.async { - self.redactReasonReceivedInvocations.append((timelineItemID: timelineItemID, reason: reason)) + self.redactReasonReceivedInvocations.append((eventOrTransactionID: eventOrTransactionID, reason: reason)) } if let redactReasonClosure = redactReasonClosure { - return await redactReasonClosure(timelineItemID, reason) + return await redactReasonClosure(eventOrTransactionID, reason) } else { return redactReasonReturnValue } @@ -14867,8 +14867,8 @@ class TimelineProxyMock: TimelineProxyProtocol { var toggleReactionToCalled: Bool { return toggleReactionToCallsCount > 0 } - var toggleReactionToReceivedArguments: (reaction: String, itemID: TimelineItemIdentifier)? - var toggleReactionToReceivedInvocations: [(reaction: String, itemID: TimelineItemIdentifier)] = [] + var toggleReactionToReceivedArguments: (reaction: String, eventID: EventOrTransactionId)? + var toggleReactionToReceivedInvocations: [(reaction: String, eventID: EventOrTransactionId)] = [] var toggleReactionToUnderlyingReturnValue: Result! var toggleReactionToReturnValue: Result! { @@ -14894,16 +14894,16 @@ class TimelineProxyMock: TimelineProxyProtocol { } } } - var toggleReactionToClosure: ((String, TimelineItemIdentifier) async -> Result)? + var toggleReactionToClosure: ((String, EventOrTransactionId) async -> Result)? - func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async -> Result { + func toggleReaction(_ reaction: String, to eventID: EventOrTransactionId) async -> Result { toggleReactionToCallsCount += 1 - toggleReactionToReceivedArguments = (reaction: reaction, itemID: itemID) + toggleReactionToReceivedArguments = (reaction: reaction, eventID: eventID) DispatchQueue.main.async { - self.toggleReactionToReceivedInvocations.append((reaction: reaction, itemID: itemID)) + self.toggleReactionToReceivedInvocations.append((reaction: reaction, eventID: eventID)) } if let toggleReactionToClosure = toggleReactionToClosure { - return await toggleReactionToClosure(reaction, itemID) + return await toggleReactionToClosure(reaction, eventID) } else { return toggleReactionToReturnValue } diff --git a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift index e4548ff0f1..52c51b578b 100644 --- a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift @@ -15,17 +15,17 @@ open class ClientSDKMock: MatrixRustSDK.Client { fileprivate var pointer: UnsafeMutableRawPointer! - //MARK: - abortOidcLogin + //MARK: - abortOidcAuth - var abortOidcLoginAuthorizationDataUnderlyingCallsCount = 0 - open var abortOidcLoginAuthorizationDataCallsCount: Int { + var abortOidcAuthAuthorizationDataUnderlyingCallsCount = 0 + open var abortOidcAuthAuthorizationDataCallsCount: Int { get { if Thread.isMainThread { - return abortOidcLoginAuthorizationDataUnderlyingCallsCount + return abortOidcAuthAuthorizationDataUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = abortOidcLoginAuthorizationDataUnderlyingCallsCount + returnValue = abortOidcAuthAuthorizationDataUnderlyingCallsCount } return returnValue! @@ -33,28 +33,28 @@ open class ClientSDKMock: MatrixRustSDK.Client { } set { if Thread.isMainThread { - abortOidcLoginAuthorizationDataUnderlyingCallsCount = newValue + abortOidcAuthAuthorizationDataUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - abortOidcLoginAuthorizationDataUnderlyingCallsCount = newValue + abortOidcAuthAuthorizationDataUnderlyingCallsCount = newValue } } } } - open var abortOidcLoginAuthorizationDataCalled: Bool { - return abortOidcLoginAuthorizationDataCallsCount > 0 + open var abortOidcAuthAuthorizationDataCalled: Bool { + return abortOidcAuthAuthorizationDataCallsCount > 0 } - open var abortOidcLoginAuthorizationDataReceivedAuthorizationData: OidcAuthorizationData? - open var abortOidcLoginAuthorizationDataReceivedInvocations: [OidcAuthorizationData] = [] - open var abortOidcLoginAuthorizationDataClosure: ((OidcAuthorizationData) async -> Void)? + open var abortOidcAuthAuthorizationDataReceivedAuthorizationData: OidcAuthorizationData? + open var abortOidcAuthAuthorizationDataReceivedInvocations: [OidcAuthorizationData] = [] + open var abortOidcAuthAuthorizationDataClosure: ((OidcAuthorizationData) async -> Void)? - open override func abortOidcLogin(authorizationData: OidcAuthorizationData) async { - abortOidcLoginAuthorizationDataCallsCount += 1 - abortOidcLoginAuthorizationDataReceivedAuthorizationData = authorizationData + open override func abortOidcAuth(authorizationData: OidcAuthorizationData) async { + abortOidcAuthAuthorizationDataCallsCount += 1 + abortOidcAuthAuthorizationDataReceivedAuthorizationData = authorizationData DispatchQueue.main.async { - self.abortOidcLoginAuthorizationDataReceivedInvocations.append(authorizationData) + self.abortOidcAuthAuthorizationDataReceivedInvocations.append(authorizationData) } - await abortOidcLoginAuthorizationDataClosure?(authorizationData) + await abortOidcAuthAuthorizationDataClosure?(authorizationData) } //MARK: - accountData @@ -3872,18 +3872,18 @@ open class ClientSDKMock: MatrixRustSDK.Client { } } - //MARK: - urlForOidcLogin + //MARK: - urlForOidc - open var urlForOidcLoginOidcConfigurationThrowableError: Error? - var urlForOidcLoginOidcConfigurationUnderlyingCallsCount = 0 - open var urlForOidcLoginOidcConfigurationCallsCount: Int { + open var urlForOidcOidcConfigurationPromptThrowableError: Error? + var urlForOidcOidcConfigurationPromptUnderlyingCallsCount = 0 + open var urlForOidcOidcConfigurationPromptCallsCount: Int { get { if Thread.isMainThread { - return urlForOidcLoginOidcConfigurationUnderlyingCallsCount + return urlForOidcOidcConfigurationPromptUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = urlForOidcLoginOidcConfigurationUnderlyingCallsCount + returnValue = urlForOidcOidcConfigurationPromptUnderlyingCallsCount } return returnValue! @@ -3891,29 +3891,29 @@ open class ClientSDKMock: MatrixRustSDK.Client { } set { if Thread.isMainThread { - urlForOidcLoginOidcConfigurationUnderlyingCallsCount = newValue + urlForOidcOidcConfigurationPromptUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - urlForOidcLoginOidcConfigurationUnderlyingCallsCount = newValue + urlForOidcOidcConfigurationPromptUnderlyingCallsCount = newValue } } } } - open var urlForOidcLoginOidcConfigurationCalled: Bool { - return urlForOidcLoginOidcConfigurationCallsCount > 0 + open var urlForOidcOidcConfigurationPromptCalled: Bool { + return urlForOidcOidcConfigurationPromptCallsCount > 0 } - open var urlForOidcLoginOidcConfigurationReceivedOidcConfiguration: OidcConfiguration? - open var urlForOidcLoginOidcConfigurationReceivedInvocations: [OidcConfiguration] = [] + open var urlForOidcOidcConfigurationPromptReceivedArguments: (oidcConfiguration: OidcConfiguration, prompt: OidcPrompt)? + open var urlForOidcOidcConfigurationPromptReceivedInvocations: [(oidcConfiguration: OidcConfiguration, prompt: OidcPrompt)] = [] - var urlForOidcLoginOidcConfigurationUnderlyingReturnValue: OidcAuthorizationData! - open var urlForOidcLoginOidcConfigurationReturnValue: OidcAuthorizationData! { + var urlForOidcOidcConfigurationPromptUnderlyingReturnValue: OidcAuthorizationData! + open var urlForOidcOidcConfigurationPromptReturnValue: OidcAuthorizationData! { get { if Thread.isMainThread { - return urlForOidcLoginOidcConfigurationUnderlyingReturnValue + return urlForOidcOidcConfigurationPromptUnderlyingReturnValue } else { var returnValue: OidcAuthorizationData? = nil DispatchQueue.main.sync { - returnValue = urlForOidcLoginOidcConfigurationUnderlyingReturnValue + returnValue = urlForOidcOidcConfigurationPromptUnderlyingReturnValue } return returnValue! @@ -3921,29 +3921,29 @@ open class ClientSDKMock: MatrixRustSDK.Client { } set { if Thread.isMainThread { - urlForOidcLoginOidcConfigurationUnderlyingReturnValue = newValue + urlForOidcOidcConfigurationPromptUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - urlForOidcLoginOidcConfigurationUnderlyingReturnValue = newValue + urlForOidcOidcConfigurationPromptUnderlyingReturnValue = newValue } } } } - open var urlForOidcLoginOidcConfigurationClosure: ((OidcConfiguration) async throws -> OidcAuthorizationData)? + open var urlForOidcOidcConfigurationPromptClosure: ((OidcConfiguration, OidcPrompt) async throws -> OidcAuthorizationData)? - open override func urlForOidcLogin(oidcConfiguration: OidcConfiguration) async throws -> OidcAuthorizationData { - if let error = urlForOidcLoginOidcConfigurationThrowableError { + open override func urlForOidc(oidcConfiguration: OidcConfiguration, prompt: OidcPrompt) async throws -> OidcAuthorizationData { + if let error = urlForOidcOidcConfigurationPromptThrowableError { throw error } - urlForOidcLoginOidcConfigurationCallsCount += 1 - urlForOidcLoginOidcConfigurationReceivedOidcConfiguration = oidcConfiguration + urlForOidcOidcConfigurationPromptCallsCount += 1 + urlForOidcOidcConfigurationPromptReceivedArguments = (oidcConfiguration: oidcConfiguration, prompt: prompt) DispatchQueue.main.async { - self.urlForOidcLoginOidcConfigurationReceivedInvocations.append(oidcConfiguration) + self.urlForOidcOidcConfigurationPromptReceivedInvocations.append((oidcConfiguration: oidcConfiguration, prompt: prompt)) } - if let urlForOidcLoginOidcConfigurationClosure = urlForOidcLoginOidcConfigurationClosure { - return try await urlForOidcLoginOidcConfigurationClosure(oidcConfiguration) + if let urlForOidcOidcConfigurationPromptClosure = urlForOidcOidcConfigurationPromptClosure { + return try await urlForOidcOidcConfigurationPromptClosure(oidcConfiguration, prompt) } else { - return urlForOidcLoginOidcConfigurationReturnValue + return urlForOidcOidcConfigurationPromptReturnValue } } @@ -19484,16 +19484,16 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { //MARK: - toggleReaction - open var toggleReactionUniqueIdKeyThrowableError: Error? - var toggleReactionUniqueIdKeyUnderlyingCallsCount = 0 - open var toggleReactionUniqueIdKeyCallsCount: Int { + open var toggleReactionItemIdKeyThrowableError: Error? + var toggleReactionItemIdKeyUnderlyingCallsCount = 0 + open var toggleReactionItemIdKeyCallsCount: Int { get { if Thread.isMainThread { - return toggleReactionUniqueIdKeyUnderlyingCallsCount + return toggleReactionItemIdKeyUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = toggleReactionUniqueIdKeyUnderlyingCallsCount + returnValue = toggleReactionItemIdKeyUnderlyingCallsCount } return returnValue! @@ -19501,31 +19501,31 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - toggleReactionUniqueIdKeyUnderlyingCallsCount = newValue + toggleReactionItemIdKeyUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - toggleReactionUniqueIdKeyUnderlyingCallsCount = newValue + toggleReactionItemIdKeyUnderlyingCallsCount = newValue } } } } - open var toggleReactionUniqueIdKeyCalled: Bool { - return toggleReactionUniqueIdKeyCallsCount > 0 + open var toggleReactionItemIdKeyCalled: Bool { + return toggleReactionItemIdKeyCallsCount > 0 } - open var toggleReactionUniqueIdKeyReceivedArguments: (uniqueId: String, key: String)? - open var toggleReactionUniqueIdKeyReceivedInvocations: [(uniqueId: String, key: String)] = [] - open var toggleReactionUniqueIdKeyClosure: ((String, String) async throws -> Void)? + open var toggleReactionItemIdKeyReceivedArguments: (itemId: EventOrTransactionId, key: String)? + open var toggleReactionItemIdKeyReceivedInvocations: [(itemId: EventOrTransactionId, key: String)] = [] + open var toggleReactionItemIdKeyClosure: ((EventOrTransactionId, String) async throws -> Void)? - open override func toggleReaction(uniqueId: String, key: String) async throws { - if let error = toggleReactionUniqueIdKeyThrowableError { + open override func toggleReaction(itemId: EventOrTransactionId, key: String) async throws { + if let error = toggleReactionItemIdKeyThrowableError { throw error } - toggleReactionUniqueIdKeyCallsCount += 1 - toggleReactionUniqueIdKeyReceivedArguments = (uniqueId: uniqueId, key: key) + toggleReactionItemIdKeyCallsCount += 1 + toggleReactionItemIdKeyReceivedArguments = (itemId: itemId, key: key) DispatchQueue.main.async { - self.toggleReactionUniqueIdKeyReceivedInvocations.append((uniqueId: uniqueId, key: key)) + self.toggleReactionItemIdKeyReceivedInvocations.append((itemId: itemId, key: key)) } - try await toggleReactionUniqueIdKeyClosure?(uniqueId, key) + try await toggleReactionItemIdKeyClosure?(itemId, key) } //MARK: - unpinEvent @@ -20724,13 +20724,13 @@ open class TimelineItemSDKMock: MatrixRustSDK.TimelineItem { return uniqueIdCallsCount > 0 } - var uniqueIdUnderlyingReturnValue: String! - open var uniqueIdReturnValue: String! { + var uniqueIdUnderlyingReturnValue: TimelineUniqueId! + open var uniqueIdReturnValue: TimelineUniqueId! { get { if Thread.isMainThread { return uniqueIdUnderlyingReturnValue } else { - var returnValue: String? = nil + var returnValue: TimelineUniqueId? = nil DispatchQueue.main.sync { returnValue = uniqueIdUnderlyingReturnValue } @@ -20748,9 +20748,9 @@ open class TimelineItemSDKMock: MatrixRustSDK.TimelineItem { } } } - open var uniqueIdClosure: (() -> String)? + open var uniqueIdClosure: (() -> TimelineUniqueId)? - open override func uniqueId() -> String { + open override func uniqueId() -> TimelineUniqueId { uniqueIdCallsCount += 1 if let uniqueIdClosure = uniqueIdClosure { return uniqueIdClosure() diff --git a/ElementX/Sources/Mocks/RoomTimelineProviderMock.swift b/ElementX/Sources/Mocks/RoomTimelineProviderMock.swift index 12a1e4806c..53e9d73c99 100644 --- a/ElementX/Sources/Mocks/RoomTimelineProviderMock.swift +++ b/ElementX/Sources/Mocks/RoomTimelineProviderMock.swift @@ -38,7 +38,7 @@ class AutoUpdatingRoomTimelineProviderMock: RoomTimelineProvider { let timelineItem = TimelineItemSDKMock() timelineItem.asEventReturnValue = EventTimelineItem.mockMessage - timelineItem.uniqueIdReturnValue = UUID().uuidString + timelineItem.uniqueIdReturnValue = .init(id: UUID().uuidString) diff.appendReturnValue = [timelineItem] diff --git a/ElementX/Sources/Mocks/SDK/ClientSDKMock.swift b/ElementX/Sources/Mocks/SDK/ClientSDKMock.swift index 61738a095f..29e80bd3dc 100644 --- a/ElementX/Sources/Mocks/SDK/ClientSDKMock.swift +++ b/ElementX/Sources/Mocks/SDK/ClientSDKMock.swift @@ -42,7 +42,7 @@ extension ClientSDKMock { userIdServerNameThrowableError = MockError.generic serverReturnValue = "https://\(configuration.serverAddress)" getUrlUrlReturnValue = configuration.elementWellKnown - urlForOidcLoginOidcConfigurationReturnValue = OidcAuthorizationDataSDKMock(configuration: configuration) + urlForOidcOidcConfigurationPromptReturnValue = OidcAuthorizationDataSDKMock(configuration: configuration) loginUsernamePasswordInitialDeviceNameDeviceIdClosure = { username, password, _, _ in guard username == configuration.validCredentials.username, password == configuration.validCredentials.password else { diff --git a/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift b/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift index 3bab9b8db4..281183664f 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift @@ -132,8 +132,12 @@ class TimelineInteractionHandler { UIPasteboard.general.url = permalinkURL } case .redact: + guard case let .event(_, eventOrTransactionID) = itemID else { + fatalError() + } + Task { - await timelineController.redact(itemID) + await timelineController.redact(eventOrTransactionID) } case .reply: guard let eventID = eventTimelineItem.id.eventID else { @@ -159,7 +163,13 @@ class TimelineInteractionHandler { case .react: displayEmojiPicker(for: itemID) case .toggleReaction(let key): - Task { await timelineController.toggleReaction(key, to: itemID) } + Task { + guard case let .event(_, eventOrTransactionID) = itemID else { + fatalError() + } + + await timelineController.toggleReaction(key, to: eventOrTransactionID) + } case .endPoll(let pollStartID): endPoll(pollStartID: pollStartID) case .pin: diff --git a/ElementX/Sources/Screens/Timeline/TimelineModels.swift b/ElementX/Sources/Screens/Timeline/TimelineModels.swift index 6bfcc9b9ca..44a7ed0834 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineModels.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineModels.swift @@ -6,6 +6,7 @@ // import Combine +import MatrixRustSDK import OrderedCollections import SwiftUI @@ -202,9 +203,9 @@ struct TimelineState { // These can be removed when we have full swiftUI and moved as @State values in the view var scrollToBottomPublisher = PassthroughSubject() - var itemsDictionary = OrderedDictionary() + var itemsDictionary = OrderedDictionary() - var uniqueIDs: [String] { + var uniqueIDs: [TimelineUniqueId] { itemsDictionary.keys.elements } diff --git a/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift b/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift index 8876749e8f..84fde96d3b 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift @@ -7,6 +7,7 @@ import Combine import Compound +import MatrixRustSDK import SwiftUI import OrderedCollections @@ -47,7 +48,7 @@ class TimelineTableViewController: UIViewController { private let coordinator: TimelineView.Coordinator private let tableView = UITableView(frame: .zero, style: .plain) - var timelineItemsDictionary = OrderedDictionary() { + var timelineItemsDictionary = OrderedDictionary() { didSet { guard canApplySnapshot else { hasPendingItems = true @@ -145,12 +146,12 @@ class TimelineTableViewController: UIViewController { @Binding private var isScrolledToBottom: Bool - private var timelineItemsIDs: [String] { + private var timelineItemsIDs: [TimelineUniqueId] { timelineItemsDictionary.keys.elements.reversed() } /// The table's diffable data source. - private var dataSource: UITableViewDiffableDataSource? + private var dataSource: UITableViewDiffableDataSource? private var cancellables = Set() /// A publisher used to throttle back pagination requests. @@ -246,7 +247,7 @@ class TimelineTableViewController: UIViewController { private func configureDataSource() { dataSource = .init(tableView: tableView) { [weak self] tableView, indexPath, id in switch id { - case TimelineTypingIndicatorCell.reuseIdentifier: + case TimelineUniqueId(id: TimelineTypingIndicatorCell.reuseIdentifier): let cell = tableView.dequeueReusableCell(withIdentifier: TimelineTypingIndicatorCell.reuseIdentifier, for: indexPath) guard let self else { return cell @@ -312,12 +313,12 @@ class TimelineTableViewController: UIViewController { private func applySnapshot() { guard let dataSource else { return } - var snapshot = NSDiffableDataSourceSnapshot() + var snapshot = NSDiffableDataSourceSnapshot() // We don't want to display the typing notification in this timeline if !coordinator.context.viewState.isPinnedEventsTimeline { snapshot.appendSections([.typingIndicator]) - snapshot.appendItems([TimelineTypingIndicatorCell.reuseIdentifier]) + snapshot.appendItems([TimelineUniqueId(id: TimelineTypingIndicatorCell.reuseIdentifier)]) } snapshot.appendSections([.main]) snapshot.appendItems(timelineItemsIDs) @@ -516,7 +517,7 @@ extension TimelineTableViewController { } /// Returns the frame of the cell for a particular timeline item. - private func cellFrame(for uniqueID: String) -> CGRect? { + private func cellFrame(for uniqueID: TimelineUniqueId) -> CGRect? { guard let timelineCell = tableView.visibleCells.first(where: { ($0 as? TimelineItemCell)?.item?.identifier.uniqueID == uniqueID }) else { return nil } @@ -536,13 +537,13 @@ extension TimelineTableViewController { } } -private extension NSDiffableDataSourceSnapshot { +private extension NSDiffableDataSourceSnapshot { var numberOfMainItems: Int { guard sectionIdentifiers.contains(.main) else { return 0 } return numberOfItems(inSection: .main) } - var mainItemIdentifiers: [String] { + var mainItemIdentifiers: [TimelineUniqueId] { guard sectionIdentifiers.contains(.main) else { return [] } return itemIdentifiers(inSection: .main) } diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index bc83d1d71c..80c2460361 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -131,8 +131,12 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { Task { await handleItemTapped(with: id) } case .itemSendInfoTapped(let itemID): handleItemSendInfoTapped(itemID: itemID) - case .toggleReaction(let emoji, let itemId): - Task { await timelineController.toggleReaction(emoji, to: itemId) } + case .toggleReaction(let emoji, let itemID): + guard case let .event(_, eventOrTransactionID) = itemID else { + fatalError() + } + + Task { await timelineController.toggleReaction(emoji, to: eventOrTransactionID) } case .sendReadReceiptIfNeeded(let lastVisibleItemID): Task { await sendReadReceiptIfNeeded(for: lastVisibleItemID) } case .paginateBackwards: @@ -639,7 +643,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { // MARK: - Timeline Item Building private func buildTimelineViews(timelineItems: [RoomTimelineItemProtocol], isSwitchingTimelines: Bool = false) { - var timelineItemsDictionary = OrderedDictionary() + var timelineItemsDictionary = OrderedDictionary() timelineItems.filter { $0 is RedactedRoomTimelineItem }.forEach { timelineItem in // Stops the audio player when a voice message is redacted. diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift index 4b6eaf2e46..63e2304ba2 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift @@ -623,7 +623,7 @@ private struct MockTimelineContent: View { } func makeItemIdentifier() -> TimelineItemIdentifier { - isPinned ? .event(uniqueID: "", eventOrTransactionID: .eventId(eventId: "pinned")) : .randomEvent + isPinned ? .event(uniqueID: .init(id: ""), eventOrTransactionID: .eventId(eventId: "pinned")) : .randomEvent } var replyDetails: TimelineItemReplyDetails? { diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift index 820cdd6423..1cafaf023e 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift @@ -79,7 +79,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { }() static let sendingLast: TextRoomTimelineItem = { - let id = viewModel.state.timelineViewState.uniqueIDs.last ?? UUID().uuidString + let id = viewModel.state.timelineViewState.uniqueIDs.last ?? .init(id: UUID().uuidString) var result = TextRoomTimelineItem(id: .event(uniqueID: id, eventOrTransactionID: .eventId(eventId: UUID().uuidString)), timestamp: "Now", isOutgoing: true, @@ -99,7 +99,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { }() static let sentLast: TextRoomTimelineItem = { - let id = viewModel.state.timelineViewState.uniqueIDs.last ?? UUID().uuidString + let id = viewModel.state.timelineViewState.uniqueIDs.last ?? .init(id: UUID().uuidString) let result = TextRoomTimelineItem(id: .event(uniqueID: id, eventOrTransactionID: .eventId(eventId: UUID().uuidString)), timestamp: "Now", isOutgoing: true, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift index 03c212504d..d7b85ce801 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift @@ -52,8 +52,8 @@ struct CollapsibleRoomTimelineView: View { struct CollapsibleRoomTimelineView_Previews: PreviewProvider, TestablePreview { static let item = CollapsibleTimelineItem(items: [ - SeparatorRoomTimelineItem(id: .virtual(uniqueID: "First separator"), text: "This is a separator"), - SeparatorRoomTimelineItem(id: .virtual(uniqueID: "Second separator"), text: "This is another separator") + SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "First separator")), text: "This is a separator"), + SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Second separator")), text: "This is another separator") ]) static var previews: some View { diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift index 8bbfaf7870..4314885116 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift @@ -33,7 +33,7 @@ struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview { static var previews: some View { VStack(alignment: .leading, spacing: 0) { - RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: "Separator"), text: "Today")), groupStyle: .single)) + RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: .init(id: "Separator")), text: "Today")), groupStyle: .single)) RoomTimelineItemView(viewState: .init(type: .text(.init(id: .randomEvent, timestamp: "", isOutgoing: true, @@ -45,7 +45,7 @@ struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview { ReadMarkerRoomTimelineView(timelineItem: item) - RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: "Separator"), text: "Today")), groupStyle: .single)) + RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: .init(id: "Separator")), text: "Today")), groupStyle: .single)) RoomTimelineItemView(viewState: .init(type: .text(.init(id: .randomEvent, timestamp: "", isOutgoing: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift index 3c6be1189c..88633b6904 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift @@ -23,7 +23,7 @@ struct SeparatorRoomTimelineView: View { struct SeparatorRoomTimelineView_Previews: PreviewProvider, TestablePreview { static var previews: some View { - let item = SeparatorRoomTimelineItem(id: .virtual(uniqueID: "Separator"), + let item = SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Separator")), text: "This is a separator") SeparatorRoomTimelineView(timelineItem: item) } diff --git a/ElementX/Sources/Services/Authentication/AuthenticationService.swift b/ElementX/Sources/Services/Authentication/AuthenticationService.swift index 092a097024..4c3307377a 100644 --- a/ElementX/Sources/Services/Authentication/AuthenticationService.swift +++ b/ElementX/Sources/Services/Authentication/AuthenticationService.swift @@ -91,7 +91,7 @@ class AuthenticationService: AuthenticationServiceProtocol { func urlForOIDCLogin() async -> Result { guard let client else { return .failure(.oidcError(.urlFailure)) } do { - let oidcData = try await client.urlForOidcLogin(oidcConfiguration: appSettings.oidcConfiguration.rustValue) + let oidcData = try await client.urlForOidc(oidcConfiguration: appSettings.oidcConfiguration.rustValue, prompt: .consent) return .success(OIDCAuthorizationDataProxy(underlyingData: oidcData)) } catch { MXLog.error("Failed to get URL for OIDC login: \(error)") @@ -102,7 +102,7 @@ class AuthenticationService: AuthenticationServiceProtocol { func abortOIDCLogin(data: OIDCAuthorizationDataProxy) async { guard let client else { return } MXLog.info("Aborting OIDC login.") - await client.abortOidcLogin(authorizationData: data.underlyingData) + await client.abortOidcAuth(authorizationData: data.underlyingData) } func loginWithOIDCCallback(_ callbackURL: URL, data: OIDCAuthorizationDataProxy) async -> Result { diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift index 7c66bbc28a..b1b4c37192 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift @@ -246,7 +246,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { var lastMessageFormattedTimestamp: String? if let latestRoomMessage = roomDetails.latestEvent { - let lastMessage = EventTimelineItemProxy(item: latestRoomMessage, uniqueID: "0") + let lastMessage = EventTimelineItemProxy(item: latestRoomMessage, uniqueID: .init(id: "0")) lastMessageFormattedTimestamp = lastMessage.timestamp.formattedMinimal() attributedLastMessage = eventStringBuilder.buildAttributedString(for: lastMessage) } diff --git a/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift b/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift index dda6752769..cf77fa6da9 100644 --- a/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift +++ b/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift @@ -10,8 +10,8 @@ import Foundation enum RoomTimelineItemFixtures { /// The default timeline items used in Xcode previews etc. static var `default`: [RoomTimelineItemProtocol] = [ - SeparatorRoomTimelineItem(id: .virtual(uniqueID: "Yesterday"), text: "Yesterday"), - TextRoomTimelineItem(id: .event(uniqueID: ".RoomTimelineItemFixtures.default.0", + SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Yesterday")), text: "Yesterday"), + TextRoomTimelineItem(id: .event(uniqueID: .init(id: ".RoomTimelineItemFixtures.default.0"), eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.0")), timestamp: "10:10 AM", isOutgoing: false, @@ -21,7 +21,7 @@ enum RoomTimelineItemFixtures { sender: .init(id: "", displayName: "Jacob"), content: .init(body: "That looks so good!"), properties: RoomTimelineItemProperties(isEdited: true)), - TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.1", + TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.1"), eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.1")), timestamp: "10:11 AM", isOutgoing: false, @@ -33,7 +33,7 @@ enum RoomTimelineItemFixtures { properties: RoomTimelineItemProperties(reactions: [ AggregatedReaction(accountOwnerID: "me", key: "🙌", senders: [ReactionSender(id: "me", timestamp: Date())]) ])), - TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.2", + TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.2"), eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.2")), timestamp: "10:11 AM", isOutgoing: false, @@ -52,8 +52,8 @@ enum RoomTimelineItemFixtures { ReactionSender(id: "jacob", timestamp: Date()) ]) ])), - SeparatorRoomTimelineItem(id: .virtual(uniqueID: "Today"), text: "Today"), - TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.3", + SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Today")), text: "Today"), + TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.3"), eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.3")), timestamp: "5 PM", isOutgoing: false, @@ -63,7 +63,7 @@ enum RoomTimelineItemFixtures { sender: .init(id: "", displayName: "Helena"), content: .init(body: "Wow, cool. Ok, lets go the usual place tomorrow?! Is that too soon? Here’s the menu, let me know what you want it’s on me!"), properties: RoomTimelineItemProperties(orderedReadReceipts: [ReadReceipt(userID: "alice", formattedTimestamp: nil)])), - TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.4", + TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.4"), eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.4")), timestamp: "5 PM", isOutgoing: true, @@ -72,7 +72,7 @@ enum RoomTimelineItemFixtures { isThreaded: false, sender: .init(id: "", displayName: "Bob"), content: .init(body: "And John's speech was amazing!")), - TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.5", + TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.5"), eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.5")), timestamp: "5 PM", isOutgoing: true, @@ -86,7 +86,7 @@ enum RoomTimelineItemFixtures { ReadReceipt(userID: "bob", formattedTimestamp: nil), ReadReceipt(userID: "charlie", formattedTimestamp: nil), ReadReceipt(userID: "dan", formattedTimestamp: nil)])), - TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.6", + TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.6"), eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.6")), timestamp: "5 PM", isOutgoing: false, @@ -242,7 +242,7 @@ enum RoomTimelineItemFixtures { static var permalinkChunk: [RoomTimelineItemProtocol] { (1...20).map { index in - TextRoomTimelineItem(id: .event(uniqueID: "\(index)", eventOrTransactionID: .eventId(eventId: "$\(index)")), + TextRoomTimelineItem(id: .event(uniqueID: .init(id: "\(index)"), eventOrTransactionID: .eventId(eventId: "$\(index)")), text: "Message ID \(index)", senderDisplayName: index > 10 ? "Alice" : "Bob") } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift index 155989b151..c5d18c0729 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift @@ -84,7 +84,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol { inReplyToEventID: String?, intentionalMentions: IntentionalMentions) async { } - func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async { } + func toggleReaction(_ reaction: String, to eventID: EventOrTransactionId) async { } func edit(_ eventOrTransactionID: EventOrTransactionId, useTimeline: Bool, @@ -92,7 +92,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol { html: String?, intentionalMentions: IntentionalMentions) async { } - func redact(_ itemID: TimelineItemIdentifier) async { } + func redact(_ eventOrTransactionID: EventOrTransactionId) async { } func pin(eventID: String) async { } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift index 0ef0dbd1d6..f9738a1735 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift @@ -158,10 +158,10 @@ class RoomTimelineController: RoomTimelineControllerProtocol { } } - func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async { - MXLog.info("Toggle reaction in \(roomID)") + func toggleReaction(_ reaction: String, to eventOrTransactionID: EventOrTransactionId) async { + MXLog.info("Toggle reaction \(reaction) to \(eventOrTransactionID)") - switch await activeTimeline.toggleReaction(reaction, to: itemID) { + switch await activeTimeline.toggleReaction(reaction, to: eventOrTransactionID) { case .success: MXLog.info("Finished toggling reaction") case .failure(let error): @@ -203,10 +203,10 @@ class RoomTimelineController: RoomTimelineControllerProtocol { } } - func redact(_ timelineItemID: TimelineItemIdentifier) async { + func redact(_ eventOrTransactionID: EventOrTransactionId) async { MXLog.info("Send redaction in \(roomID)") - switch await activeTimeline.redact(timelineItemID, reason: nil) { + switch await activeTimeline.redact(eventOrTransactionID, reason: nil) { case .success: MXLog.info("Finished redacting message") case .failure(let error): diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift index be7c635585..4b47bacf44 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift @@ -57,9 +57,9 @@ protocol RoomTimelineControllerProtocol { html: String?, intentionalMentions: IntentionalMentions) async - func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async + func toggleReaction(_ reaction: String, to eventOrTransactionID: EventOrTransactionId) async - func redact(_ itemID: TimelineItemIdentifier) async + func redact(_ eventOrTransactionID: EventOrTransactionId) async func pin(eventID: String) async diff --git a/ElementX/Sources/Services/Timeline/TimelineItemIdentifier.swift b/ElementX/Sources/Services/Timeline/TimelineItemIdentifier.swift index 1770dbb493..a6efccb371 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItemIdentifier.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItemIdentifier.swift @@ -14,10 +14,10 @@ import MatrixRustSDK /// - eventOrTransactionID: Contains the 2 possible identifiers of an event, either it has a remote event id or /// a local transaction id, never both or none. enum TimelineItemIdentifier: Hashable { - case event(uniqueID: String, eventOrTransactionID: EventOrTransactionId) - case virtual(uniqueID: String) + case event(uniqueID: TimelineUniqueId, eventOrTransactionID: EventOrTransactionId) + case virtual(uniqueID: TimelineUniqueId) - var uniqueID: String { + var uniqueID: TimelineUniqueId { switch self { case .event(let uniqueID, _): return uniqueID @@ -57,10 +57,10 @@ enum TimelineItemIdentifier: Hashable { extension TimelineItemIdentifier { static var randomEvent: Self { - .event(uniqueID: UUID().uuidString, eventOrTransactionID: .eventId(eventId: UUID().uuidString)) + .event(uniqueID: .init(id: UUID().uuidString), eventOrTransactionID: .eventId(eventId: UUID().uuidString)) } static var randomVirtual: Self { - .virtual(uniqueID: UUID().uuidString) + .virtual(uniqueID: .init(id: UUID().uuidString)) } } diff --git a/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift b/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift index ef956e8068..bb939082f8 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift @@ -11,14 +11,14 @@ import MatrixRustSDK /// A light wrapper around timeline items returned from Rust. enum TimelineItemProxy { case event(EventTimelineItemProxy) - case virtual(MatrixRustSDK.VirtualTimelineItem, uniqueID: String) + case virtual(MatrixRustSDK.VirtualTimelineItem, uniqueID: TimelineUniqueId) case unknown(MatrixRustSDK.TimelineItem) init(item: MatrixRustSDK.TimelineItem) { if let eventItem = item.asEvent() { - self = .event(EventTimelineItemProxy(item: eventItem, uniqueID: String(item.uniqueId()))) + self = .event(EventTimelineItemProxy(item: eventItem, uniqueID: item.uniqueId())) } else if let virtualItem = item.asVirtual() { - self = .virtual(virtualItem, uniqueID: String(item.uniqueId())) + self = .virtual(virtualItem, uniqueID: item.uniqueId()) } else { self = .unknown(item) } @@ -71,7 +71,7 @@ class EventTimelineItemProxy { let item: MatrixRustSDK.EventTimelineItem let id: TimelineItemIdentifier - init(item: MatrixRustSDK.EventTimelineItem, uniqueID: String) { + init(item: MatrixRustSDK.EventTimelineItem, uniqueID: TimelineUniqueId) { self.item = item id = .event(uniqueID: uniqueID, eventOrTransactionID: item.eventOrTransactionId) diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift index 319d024476..139a50eb3e 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift @@ -22,6 +22,6 @@ struct PaginationIndicatorRoomTimelineItem: DecorationTimelineItemProtocol, Equa } init(position: Position) { - id = .virtual(uniqueID: position.id) + id = .virtual(uniqueID: .init(id: position.id)) } } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/TimelineStartRoomTimelineItem.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/TimelineStartRoomTimelineItem.swift index 33a601445f..44237552fe 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/TimelineStartRoomTimelineItem.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/TimelineStartRoomTimelineItem.swift @@ -8,6 +8,6 @@ import Foundation struct TimelineStartRoomTimelineItem: DecorationTimelineItemProtocol, Equatable { - let id: TimelineItemIdentifier = .virtual(uniqueID: UUID().uuidString) + let id: TimelineItemIdentifier = .virtual(uniqueID: .init(id: UUID().uuidString)) let name: String? } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemViewState.swift b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemViewState.swift index 5d5322a4d0..94654d2df4 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemViewState.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemViewState.swift @@ -6,6 +6,7 @@ // import Foundation +import MatrixRustSDK final class RoomTimelineItemViewState: Identifiable, Equatable, ObservableObject { @Published var type: RoomTimelineItemType @@ -34,7 +35,7 @@ final class RoomTimelineItemViewState: Identifiable, Equatable, ObservableObject // MARK: Identifiable /// The `timelineID` of the item, used for the timeline view level identification, do not use for any business logic use `identifier` instead - var id: String { + var id: TimelineUniqueId { identifier.uniqueID } } diff --git a/ElementX/Sources/Services/Timeline/TimelineProxy.swift b/ElementX/Sources/Services/Timeline/TimelineProxy.swift index 74fe851ae4..160eff7911 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxy.swift @@ -179,22 +179,17 @@ final class TimelineProxy: TimelineProxyProtocol { } } - func redact(_ timelineItemID: TimelineItemIdentifier, reason: String?) async -> Result { - MXLog.info("Redacting timeline item: \(timelineItemID)") - - guard let eventOrTransactionID = await timelineProvider.itemProxies.firstEventTimelineItemUsingStableID(timelineItemID)?.eventOrTransactionId else { - MXLog.error("Unknown timeline item: \(timelineItemID)") - return .failure(.failedRedacting) - } + func redact(_ eventOrTransactionID: EventOrTransactionId, reason: String?) async -> Result { + MXLog.info("Redacting timeline item: \(eventOrTransactionID)") do { try await timeline.redactEvent(eventOrTransactionId: eventOrTransactionID, reason: reason) - MXLog.info("Redacted timeline item: \(timelineItemID)") + MXLog.info("Redacted timeline item: \(eventOrTransactionID)") return .success(()) } catch { - MXLog.error("Failed redacting timeline item: \(timelineItemID) with error: \(error)") + MXLog.error("Failed redacting timeline item: \(eventOrTransactionID) with error: \(error)") return .failure(.sdkError(error)) } } @@ -426,15 +421,15 @@ final class TimelineProxy: TimelineProxyProtocol { } } - func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async -> Result { - MXLog.info("Toggling reaction for event: \(itemID)") + func toggleReaction(_ reaction: String, to eventOrTransactionID: EventOrTransactionId) async -> Result { + MXLog.info("Toggling reaction \(reaction) for event: \(eventOrTransactionID)") do { - try await timeline.toggleReaction(uniqueId: itemID.uniqueID, key: reaction) - MXLog.info("Finished toggling reaction for event: \(itemID)") + try await timeline.toggleReaction(itemId: eventOrTransactionID, key: reaction) + MXLog.info("Finished toggling reaction for event: \(eventOrTransactionID)") return .success(()) } catch { - MXLog.error("Failed toggling reaction for event: \(itemID)") + MXLog.error("Failed toggling reaction for event: \(eventOrTransactionID)") return .failure(.sdkError(error)) } } diff --git a/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift index 0f18fa5db0..9301314132 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift @@ -41,7 +41,7 @@ protocol TimelineProxyProtocol { func edit(_ eventOrTransactionID: EventOrTransactionId, newContent: RoomMessageEventContentWithoutRelation) async -> Result - func redact(_ timelineItemID: TimelineItemIdentifier, + func redact(_ eventOrTransactionID: EventOrTransactionId, reason: String?) async -> Result func pin(eventID: String) async -> Result @@ -93,7 +93,7 @@ protocol TimelineProxyProtocol { inReplyToEventID: String?, intentionalMentions: IntentionalMentions) async -> Result - func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async -> Result + func toggleReaction(_ reaction: String, to eventID: EventOrTransactionId) async -> Result func createPoll(question: String, answers: [String], pollKind: Poll.Kind) async -> Result diff --git a/UnitTests/Sources/LoggingTests.swift b/UnitTests/Sources/LoggingTests.swift index 6887809a5a..20205e5209 100644 --- a/UnitTests/Sources/LoggingTests.swift +++ b/UnitTests/Sources/LoggingTests.swift @@ -195,25 +195,25 @@ class LoggingTests: XCTestCase { } let content = try String(contentsOf: logFile) - XCTAssertTrue(content.contains(textMessage.id.uniqueID)) + XCTAssertTrue(content.contains(textMessage.id.uniqueID.id)) XCTAssertFalse(content.contains(textMessage.body)) XCTAssertFalse(content.contains(textAttributedString)) - XCTAssertTrue(content.contains(noticeMessage.id.uniqueID)) + XCTAssertTrue(content.contains(noticeMessage.id.uniqueID.id)) XCTAssertFalse(content.contains(noticeMessage.body)) XCTAssertFalse(content.contains(noticeAttributedString)) - XCTAssertTrue(content.contains(emoteMessage.id.uniqueID)) + XCTAssertTrue(content.contains(emoteMessage.id.uniqueID.id)) XCTAssertFalse(content.contains(emoteMessage.body)) XCTAssertFalse(content.contains(emoteAttributedString)) - XCTAssertTrue(content.contains(imageMessage.id.uniqueID)) + XCTAssertTrue(content.contains(imageMessage.id.uniqueID.id)) XCTAssertFalse(content.contains(imageMessage.body)) - XCTAssertTrue(content.contains(videoMessage.id.uniqueID)) + XCTAssertTrue(content.contains(videoMessage.id.uniqueID.id)) XCTAssertFalse(content.contains(videoMessage.body)) - XCTAssertTrue(content.contains(fileMessage.id.uniqueID)) + XCTAssertTrue(content.contains(fileMessage.id.uniqueID.id)) XCTAssertFalse(content.contains(fileMessage.body)) } diff --git a/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift b/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift index e96a8e2402..3076a40aa7 100644 --- a/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift +++ b/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift @@ -12,7 +12,7 @@ import XCTest @MainActor class MessageForwardingScreenViewModelTests: XCTestCase { - let forwardingItem = MessageForwardingItem(id: .event(uniqueID: "t1", eventOrTransactionID: .eventId(eventId: "t1")), + let forwardingItem = MessageForwardingItem(id: .event(uniqueID: .init(id: "t1"), eventOrTransactionID: .eventId(eventId: "t1")), roomID: "1", content: .init(noPointer: .init())) var viewModel: MessageForwardingScreenViewModelProtocol! diff --git a/UnitTests/Sources/RoomScreenViewModelTests.swift b/UnitTests/Sources/RoomScreenViewModelTests.swift index 43b8cf10c9..e21891bb43 100644 --- a/UnitTests/Sources/RoomScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomScreenViewModelTests.swift @@ -69,8 +69,8 @@ class RoomScreenViewModelTests: XCTestCase { let providerUpdateSubject = PassthroughSubject<([TimelineItemProxy], PaginationState), Never>() pinnedTimelineProviderMock.underlyingUpdatePublisher = providerUpdateSubject.eraseToAnyPublisher() pinnedTimelineMock.timelineProvider = pinnedTimelineProviderMock - pinnedTimelineProviderMock.itemProxies = [.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test1")), uniqueID: "1")), - .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test2")), uniqueID: "2"))] + pinnedTimelineProviderMock.itemProxies = [.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test1")), uniqueID: .init(id: "1"))), + .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test2")), uniqueID: .init(id: "2")))] // check if the banner is now in a loaded state and is showing the counter deferred = deferFulfillment(viewModel.context.$viewState) { viewState in @@ -86,9 +86,9 @@ class RoomScreenViewModelTests: XCTestCase { deferred = deferFulfillment(viewModel.context.$viewState) { viewState in viewState.pinnedEventsBannerState.count == 3 } - providerUpdateSubject.send(([.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test1")), uniqueID: "1")), - .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test2")), uniqueID: "2")), - .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test3")), uniqueID: "3"))], .initial)) + providerUpdateSubject.send(([.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test1")), uniqueID: .init(id: "1"))), + .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test2")), uniqueID: .init(id: "2"))), + .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test3")), uniqueID: .init(id: "3")))], .initial)) try await deferred.fulfill() XCTAssertFalse(viewModel.context.viewState.pinnedEventsBannerState.isLoading) XCTAssertTrue(viewModel.context.viewState.shouldShowPinnedEventsBanner) @@ -109,9 +109,9 @@ class RoomScreenViewModelTests: XCTestCase { let pinnedTimelineProviderMock = RoomTimelineProviderMock() pinnedTimelineMock.timelineProvider = pinnedTimelineProviderMock pinnedTimelineProviderMock.underlyingUpdatePublisher = Empty<([TimelineItemProxy], PaginationState), Never>().eraseToAnyPublisher() - pinnedTimelineProviderMock.itemProxies = [.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test1")), uniqueID: "1")), - .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test2")), uniqueID: "2")), - .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test3")), uniqueID: "3"))] + pinnedTimelineProviderMock.itemProxies = [.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test1")), uniqueID: .init(id: "1"))), + .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test2")), uniqueID: .init(id: "2"))), + .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test3")), uniqueID: .init(id: "3")))] roomProxyMock.underlyingPinnedEventsTimeline = pinnedTimelineMock let viewModel = RoomScreenViewModel(clientProxy: ClientProxyMock(), roomProxy: roomProxyMock, diff --git a/UnitTests/Sources/TimelineItemFactoryTests.swift b/UnitTests/Sources/TimelineItemFactoryTests.swift index 0ca14f8fac..9033adda4e 100644 --- a/UnitTests/Sources/TimelineItemFactoryTests.swift +++ b/UnitTests/Sources/TimelineItemFactoryTests.swift @@ -22,7 +22,7 @@ class TimelineItemFactoryTests: XCTestCase { let eventTimelineItem = EventTimelineItem.mockCallInvite(sender: senderUserID) - let eventTimelineItemProxy = EventTimelineItemProxy(item: eventTimelineItem, uniqueID: "0") + let eventTimelineItemProxy = EventTimelineItemProxy(item: eventTimelineItem, uniqueID: .init(id: "0")) let item = factory.buildTimelineItem(for: eventTimelineItemProxy, isDM: false) diff --git a/UnitTests/Sources/TimelineViewModelTests.swift b/UnitTests/Sources/TimelineViewModelTests.swift index 6625562c3f..d0a95e8131 100644 --- a/UnitTests/Sources/TimelineViewModelTests.swift +++ b/UnitTests/Sources/TimelineViewModelTests.swift @@ -8,6 +8,7 @@ @testable import ElementX import Combine +import MatrixRustSDK import XCTest @MainActor @@ -258,9 +259,9 @@ class TimelineViewModelTests: XCTestCase { func testSendReadReceiptWithoutEvents() async throws { // Given a room with only virtual items. - let items = [SeparatorRoomTimelineItem(uniqueID: "v1"), - SeparatorRoomTimelineItem(uniqueID: "v2"), - SeparatorRoomTimelineItem(uniqueID: "v3")] + let items = [SeparatorRoomTimelineItem(uniqueID: .init(id: "v1")), + SeparatorRoomTimelineItem(uniqueID: .init(id: "v2")), + SeparatorRoomTimelineItem(uniqueID: .init(id: "v3"))] let (viewModel, _, timelineProxy, _) = readReceiptsConfiguration(with: items) // When sending a read receipt for the last item. @@ -275,7 +276,7 @@ class TimelineViewModelTests: XCTestCase { // Given a room where the last event is a virtual item. let items: [RoomTimelineItemProtocol] = [TextRoomTimelineItem(eventID: "t1"), TextRoomTimelineItem(eventID: "t2"), - SeparatorRoomTimelineItem(uniqueID: "v3")] + SeparatorRoomTimelineItem(uniqueID: .init(id: "v3"))] let (viewModel, _, _, _) = readReceiptsConfiguration(with: items) // When sending a read receipt for the last item. @@ -436,14 +437,14 @@ private extension TextRoomTimelineItem { } private extension SeparatorRoomTimelineItem { - init(uniqueID: String) { + init(uniqueID: TimelineUniqueId) { self.init(id: .virtual(uniqueID: uniqueID), text: "") } } private extension TextRoomTimelineItem { init(eventID: String) { - self.init(id: .event(uniqueID: UUID().uuidString, eventOrTransactionID: .eventId(eventId: eventID)), + self.init(id: .event(uniqueID: .init(id: UUID().uuidString), eventOrTransactionID: .eventId(eventId: eventID)), timestamp: "", isOutgoing: false, isEditable: false, diff --git a/project.yml b/project.yml index 1064f024d8..f7fde5a70e 100644 --- a/project.yml +++ b/project.yml @@ -60,7 +60,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/element-hq/matrix-rust-components-swift - exactVersion: 1.0.57 + exactVersion: 1.0.58 # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios From 62652172468d69ba82ab27007104e55863c23b09 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 16 Oct 2024 19:52:11 +0300 Subject: [PATCH 063/114] Remove superfluous media request upload handle cancellation call. --- .../MediaUploadPreviewScreenViewModel.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenViewModel.swift b/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenViewModel.swift index bc46ef4dbe..d1610f673f 100644 --- a/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenViewModel.swift +++ b/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenViewModel.swift @@ -77,7 +77,6 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType, private func sendAttachment(mediaInfo: MediaInfo, progressSubject: CurrentValueSubject) async -> Result { let requestHandle: ((SendAttachmentJoinHandleProtocol) -> Void) = { [weak self] handle in - self?.requestHandle?.cancel() self?.requestHandle = handle } From 1723542d6af3fb801bf6b769c43af19dae4e32e8 Mon Sep 17 00:00:00 2001 From: Doug Date: Thu, 17 Oct 2024 12:45:44 +0100 Subject: [PATCH 064/114] Fix an error message in BuildSDK. --- Tools/Sources/BuildSDK.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/Sources/BuildSDK.swift b/Tools/Sources/BuildSDK.swift index 7a6346f503..156cbfb227 100644 --- a/Tools/Sources/BuildSDK.swift +++ b/Tools/Sources/BuildSDK.swift @@ -46,7 +46,7 @@ struct BuildSDK: ParsableCommand { Rust is missing the necessary targets to build the SDK. Run the following command to install them: - rustup target add \(missingTargets.joined(separator: " ")) --toolchain nightly + rustup target add \(missingTargets.joined(separator: " ")) """ default: From bd4ecdd060f5687a6c1c580bb6213202ffc30eb5 Mon Sep 17 00:00:00 2001 From: Mauro <34335419+Velin92@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:00:51 +0200 Subject: [PATCH 065/114] Knock and knocked state for the join room screen (#3424) * JoinRoomScreen ui for knocking * code improvement * updated previews * added knocked state with tests * send knock request * Apply suggestions from code review Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com> * pr comments --------- Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com> --- ElementX.xcodeproj/project.pbxproj | 48 ++-- .../en.lproj/Localizable.strings | 7 +- .../RoomFlowCoordinator.swift | 3 +- ElementX/Sources/Generated/Strings.swift | 12 +- .../Mocks/Generated/GeneratedMocks.swift | 251 ++++++++++++++++++ .../Sources/Mocks/KnockedRoomProxyMock.swift | 29 ++ .../Sources/Other/Extensions/String.swift | 7 + .../JoinRoomScreenCoordinator.swift | 2 + .../JoinRoomScreen/JoinRoomScreenModels.swift | 4 + .../JoinRoomScreenViewModel.swift | 52 +++- .../JoinRoomScreen/View/JoinRoomScreen.swift | 82 +++++- .../Sources/Services/Client/ClientProxy.swift | 34 ++- .../Services/Client/ClientProxyProtocol.swift | 4 + .../Services/Room/KnockedRoomProxy.swift | 82 ++++++ .../Services/Room/RoomProxyProtocol.swift | 6 + .../test_joinRoomScreen-iPad-en-GB.Knock.png | 4 +- ...test_joinRoomScreen-iPad-en-GB.Knocked.png | 3 + .../test_joinRoomScreen-iPad-pseudo.Knock.png | 4 +- ...est_joinRoomScreen-iPad-pseudo.Knocked.png | 3 + ...t_joinRoomScreen-iPhone-16-en-GB.Knock.png | 4 +- ...joinRoomScreen-iPhone-16-en-GB.Knocked.png | 3 + ..._joinRoomScreen-iPhone-16-pseudo.Knock.png | 4 +- ...oinRoomScreen-iPhone-16-pseudo.Knocked.png | 3 + .../JoinRoomScreenViewModelTests.swift | 24 +- 24 files changed, 627 insertions(+), 48 deletions(-) create mode 100644 ElementX/Sources/Mocks/KnockedRoomProxyMock.swift create mode 100644 ElementX/Sources/Services/Room/KnockedRoomProxy.swift create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knocked.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knocked.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knocked.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knocked.png diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 56e4832517..dfda797d7f 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 56; objects = { /* Begin PBXAggregateTarget section */ @@ -767,6 +767,8 @@ A6D4C5EEA85A6A0ABA1559D6 /* RoomDetailsEditScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16D09C79746BDCD9173EB3A7 /* RoomDetailsEditScreenModels.swift */; }; A6DEC1ADEC8FEEC206A0FA37 /* AttributedStringBuilderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72F37B5DA798C9AE436F2C2C /* AttributedStringBuilderProtocol.swift */; }; A6F345328CCC5C9B0DAE2257 /* LogViewerScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BB05221D7D941CC82DC8480 /* LogViewerScreenViewModel.swift */; }; + A71F957D2CBFF33100FDBDF2 /* KnockedRoomProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71F957C2CBFF32A00FDBDF2 /* KnockedRoomProxy.swift */; }; + A71F957F2CBFFD2500FDBDF2 /* KnockedRoomProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71F957E2CBFFD1F00FDBDF2 /* KnockedRoomProxyMock.swift */; }; A722F426FD81FC67706BB1E0 /* CustomLayoutLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42236480CF0431535EBE8387 /* CustomLayoutLabelStyle.swift */; }; A74438ED16F8683A4B793E6A /* AnalyticsSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BCE3FAF40932AC7C7639AC4 /* AnalyticsSettingsScreenViewModel.swift */; }; A7D48E44D485B143AADDB77D /* Strings+Untranslated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */; }; @@ -1213,13 +1215,13 @@ 033DB41C51865A2E83174E87 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = ""; }; 035177BCD8E8308B098AC3C2 /* WindowManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowManager.swift; sourceTree = ""; }; 0376C429FAB1687C3D905F3E /* MockCoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCoder.swift; sourceTree = ""; }; - 0392E3FDE372C9B56FEEED8B /* test_voice_message.m4a */ = {isa = PBXFileReference; path = test_voice_message.m4a; sourceTree = ""; }; + 0392E3FDE372C9B56FEEED8B /* test_voice_message.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_voice_message.m4a; sourceTree = ""; }; 03DD998E523D4EC93C7ED703 /* RoomNotificationSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenViewModelProtocol.swift; sourceTree = ""; }; 03FABD73FD8086EFAB699F42 /* MediaUploadPreviewScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModelTests.swift; sourceTree = ""; }; 044E501B8331B339874D1B96 /* CompoundIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompoundIcon.swift; sourceTree = ""; }; 045253F9967A535EE5B16691 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; 046C0D3F53B0B5EF0A1F5BEA /* RoomSummaryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryTests.swift; sourceTree = ""; }; - 048A21188AB19349D026BECD /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; + 048A21188AB19349D026BECD /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 04BB8DDE245ED86C489BA983 /* AccessibilityIdentifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityIdentifiers.swift; sourceTree = ""; }; 04DF593C3F7AF4B2FBAEB05D /* FileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManager.swift; sourceTree = ""; }; 0516C69708D5CBDE1A8E77EC /* RoomDirectorySearchProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchProxyProtocol.swift; sourceTree = ""; }; @@ -1285,7 +1287,7 @@ 127A57D053CE8C87B5EFB089 /* Consumable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Consumable.swift; sourceTree = ""; }; 127C8472672A5BA09EF1ACF8 /* CurrentValuePublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentValuePublisher.swift; sourceTree = ""; }; 128501375217576AF0FE3E92 /* RoomAttachmentPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomAttachmentPicker.swift; sourceTree = ""; }; - 1304D9191300873EADA52D6E /* IntegrationTests.xctestplan */ = {isa = PBXFileReference; path = IntegrationTests.xctestplan; sourceTree = ""; }; + 1304D9191300873EADA52D6E /* IntegrationTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = IntegrationTests.xctestplan; sourceTree = ""; }; 130ED565A078F7E0B59D9D25 /* UNTextInputNotificationResponse+Creator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UNTextInputNotificationResponse+Creator.swift"; sourceTree = ""; }; 136F80A613B55BDD071DCEA5 /* JoinRoomScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenModels.swift; sourceTree = ""; }; 13802897C7AFA360EA74C0B0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = ""; }; @@ -1374,7 +1376,7 @@ 25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationItemProxy.swift; sourceTree = ""; }; 25F8664F1FB95AF3C4202478 /* PollFormScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenCoordinator.swift; sourceTree = ""; }; 260004737C573A56FA01E86E /* Encodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encodable.swift; sourceTree = ""; }; - 267BB1D5B08A9511F894CB57 /* PreviewTests.xctestplan */ = {isa = PBXFileReference; path = PreviewTests.xctestplan; sourceTree = ""; }; + 267BB1D5B08A9511F894CB57 /* PreviewTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = PreviewTests.xctestplan; sourceTree = ""; }; 26B0A96B8FE4849227945067 /* VoiceMessageRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRecorder.swift; sourceTree = ""; }; 26EAAB54C6CE91D64B69A9F8 /* AppLockServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockServiceProtocol.swift; sourceTree = ""; }; 2721D7B051F0159AA919DA05 /* RoomChangePermissionsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangePermissionsScreenViewModelProtocol.swift; sourceTree = ""; }; @@ -1445,7 +1447,7 @@ 3558A15CFB934F9229301527 /* RestorationToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestorationToken.swift; sourceTree = ""; }; 35AFCF4C05DEED04E3DB1A16 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; 35FA991289149D31F4286747 /* UserPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreference.swift; sourceTree = ""; }; - 36DA824791172B9821EACBED /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; + 36DA824791172B9821EACBED /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 36FD673E24FBFCFDF398716A /* RoomMemberProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberProxyMock.swift; sourceTree = ""; }; 376D941BF8BB294389C0DE24 /* MapTilerURLBuildersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerURLBuildersTests.swift; sourceTree = ""; }; 37A243E04B58DC6E41FDCD82 /* EmojiItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiItem.swift; sourceTree = ""; }; @@ -1549,7 +1551,7 @@ 4B41FABA2B0AEF4389986495 /* LoginMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginMode.swift; sourceTree = ""; }; 4BD371B60E07A5324B9507EF /* AnalyticsSettingsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenCoordinator.swift; sourceTree = ""; }; 4C8D988E82A8DFA13BE46F7C /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = pl; path = pl.lproj/Localizable.stringsdict; sourceTree = ""; }; - 4CD6AC7546E8D7E5C73CEA48 /* ElementX.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = ElementX.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CD6AC7546E8D7E5C73CEA48 /* ElementX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ElementX.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4CDDDDD9FE1A699D23A5E096 /* LoginScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreen.swift; sourceTree = ""; }; 4D3A7375AB22721C436EB056 /* ComposerToolbarModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerToolbarModels.swift; sourceTree = ""; }; 4E2245243369B99216C7D84E /* ImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; sourceTree = ""; }; @@ -1815,7 +1817,7 @@ 8D55702474F279D910D2D162 /* RoomStateEventStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomStateEventStringBuilder.swift; sourceTree = ""; }; 8D8169443E5AC5FF71BFB3DB /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; 8DA1E8F287680C8ED25EDBAC /* NetworkMonitorMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitorMock.swift; sourceTree = ""; }; - 8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; path = UITests.xctestplan; sourceTree = ""; }; + 8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UITests.xctestplan; sourceTree = ""; }; 8E1584F8BCF407BB94F48F04 /* EncryptionResetPasswordScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetPasswordScreen.swift; sourceTree = ""; }; 8EAF4A49F3ACD8BB8B0D2371 /* ClientSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientSDKMock.swift; sourceTree = ""; }; 8F21ED7205048668BEB44A38 /* AppActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppActivityView.swift; sourceTree = ""; }; @@ -1917,6 +1919,8 @@ A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHGPostHogConfiguration.swift; sourceTree = ""; }; A6C11AD9813045E44F950410 /* ElementCallWidgetDriverProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallWidgetDriverProtocol.swift; sourceTree = ""; }; A6EA0D8B0BBD8805F7D5A133 /* TextBasedRoomTimelineViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextBasedRoomTimelineViewProtocol.swift; sourceTree = ""; }; + A71F957C2CBFF32A00FDBDF2 /* KnockedRoomProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnockedRoomProxy.swift; sourceTree = ""; }; + A71F957E2CBFFD1F00FDBDF2 /* KnockedRoomProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnockedRoomProxyMock.swift; sourceTree = ""; }; A73A07BAEDD74C48795A996A /* AsyncSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncSequence.swift; sourceTree = ""; }; A7978C9EFBDD7DE39BD86726 /* RestorationTokenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestorationTokenTests.swift; sourceTree = ""; }; A7C4EA55DA62F9D0F984A2AE /* CollapsibleTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleTimelineItem.swift; sourceTree = ""; }; @@ -1991,7 +1995,7 @@ B50F03079F6B5EF9CA005F14 /* TimelineProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineProxyProtocol.swift; sourceTree = ""; }; B53AC78E49A297AC1D72A7CF /* AppMediator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediator.swift; sourceTree = ""; }; B590BD4507D4F0A377FDE01A /* LoadableAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadableAvatarImage.swift; sourceTree = ""; }; - B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; path = ConfettiScene.scn; sourceTree = ""; }; + B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = ConfettiScene.scn; sourceTree = ""; }; B63B69F9A2BC74DD40DC75C8 /* AdvancedSettingsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenViewModel.swift; sourceTree = ""; }; B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetails.swift; sourceTree = ""; }; B655A536341D2695158C6664 /* AuthenticationClientBuilderFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationClientBuilderFactory.swift; sourceTree = ""; }; @@ -2106,7 +2110,7 @@ CDB3227C7A74B734924942E9 /* RoomSummaryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryProvider.swift; sourceTree = ""; }; CDE3F3911FF7CC639BDE5844 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; CEE20623EB4A9B88FB29F2BA /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/SAS.strings; sourceTree = ""; }; - CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; path = UnitTests.xctestplan; sourceTree = ""; }; + CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UnitTests.xctestplan; sourceTree = ""; }; D071F86CD47582B9196C9D16 /* UserDiscoverySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDiscoverySection.swift; sourceTree = ""; }; D086854995173E897F993C26 /* AdvancedSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenViewModelProtocol.swift; sourceTree = ""; }; D09A267106B9585D3D0CFC0D /* ClientError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientError.swift; sourceTree = ""; }; @@ -2237,7 +2241,7 @@ ED0CBEAB5F796BEFBAF7BB6A /* VideoRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineView.swift; sourceTree = ""; }; ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemProtocol.swift; sourceTree = ""; }; ED33988DA4FD4FC666800106 /* SessionVerificationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreenViewModel.swift; sourceTree = ""; }; - ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; path = message.caf; sourceTree = ""; }; + ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = message.caf; sourceTree = ""; }; ED49073BB1C1FC649DAC2CCD /* LocationRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationRoomTimelineView.swift; sourceTree = ""; }; ED60E4D2CD678E1EBF16F77A /* BlockedUsersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedUsersScreen.swift; sourceTree = ""; }; EE378083653EF0C9B5E9D580 /* EmoteRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmoteRoomTimelineItemContent.swift; sourceTree = ""; }; @@ -2259,7 +2263,7 @@ F174A5627CDB3CAF280D1880 /* EmojiPickerScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerScreenModels.swift; sourceTree = ""; }; F17EFA1D3D09FC2F9C5E1CB2 /* MediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProvider.swift; sourceTree = ""; }; F1B8500C152BC59445647DA8 /* UnsupportedRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsupportedRoomTimelineItem.swift; sourceTree = ""; }; - F2D513D2477B57F90E98EEC0 /* portrait_test_video.mp4 */ = {isa = PBXFileReference; path = portrait_test_video.mp4; sourceTree = ""; }; + F2D513D2477B57F90E98EEC0 /* portrait_test_video.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = portrait_test_video.mp4; sourceTree = ""; }; F2E4EF80DFB8FE7C4469B15D /* RoomDirectorySearchScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreen.swift; sourceTree = ""; }; F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixEntityRegexTests.swift; sourceTree = ""; }; F348B5F2C12F9D4F4B4D3884 /* VideoRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineItem.swift; sourceTree = ""; }; @@ -2899,6 +2903,7 @@ 31CE4DA53232AA534057F912 /* Mocks */ = { isa = PBXGroup; children = ( + A71F957E2CBFFD1F00FDBDF2 /* KnockedRoomProxyMock.swift */, 69CB8242D69B7E4D0B32E18D /* AggregatedReactionMock.swift */, 3BAC027034248429A438886B /* AppMediatorMock.swift */, 0554FEA301486A8CFA475D5A /* AuthenticationClientBuilderFactoryMock.swift */, @@ -3195,6 +3200,7 @@ 40E6246F03D1FE377BC5D963 /* Room */ = { isa = PBXGroup; children = ( + A71F957C2CBFF32A00FDBDF2 /* KnockedRoomProxy.swift */, 0E95B3BDB80531C85CD50AE6 /* InvitedRoomProxy.swift */, 07C6B0B087FE6601C3F77816 /* JoinedRoomProxy.swift */, B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */, @@ -6450,6 +6456,7 @@ 46A6DB0F78FB399BD59E2D41 /* EncryptionKeyProviderProtocol.swift in Sources */, 0C6DF318E9C8F6461E6ABDE7 /* EncryptionResetPasswordScreen.swift in Sources */, 36926D795D6D19177C7812F8 /* EncryptionResetPasswordScreenCoordinator.swift in Sources */, + A71F957D2CBFF33100FDBDF2 /* KnockedRoomProxy.swift in Sources */, B1B255CE0E4306DD6E09D936 /* EncryptionResetPasswordScreenModels.swift in Sources */, D6152E21036B88C44ECB22E7 /* EncryptionResetPasswordScreenViewModel.swift in Sources */, A0601810597769B81C2358AF /* EncryptionResetPasswordScreenViewModelProtocol.swift in Sources */, @@ -6607,6 +6614,7 @@ EA01A06EEDFEF4AE7652E5F3 /* NSRegularExpresion.swift in Sources */, FA2BBAE9FC5E2E9F960C0980 /* NavigationCoordinators.swift in Sources */, 71C1347F23868324A4F43940 /* NavigationModule.swift in Sources */, + A71F957F2CBFFD2500FDBDF2 /* KnockedRoomProxyMock.swift in Sources */, B5E455C9689EA600EDB3E9E0 /* NavigationRootCoordinator.swift in Sources */, 93BAF04D9CCBC0A8841414D0 /* NetworkMonitor.swift in Sources */, 96B3606E30F824095B1DD022 /* NetworkMonitorMock.swift in Sources */, @@ -7277,9 +7285,7 @@ "@executable_path/../../Frameworks", ); MARKETING_VERSION = "$(MARKETING_VERSION)"; - OTHER_SWIFT_FLAGS = ( - "-DIS_NSE", - ); + OTHER_SWIFT_FLAGS = "-DIS_NSE"; PRODUCT_BUNDLE_IDENTIFIER = "${BASE_BUNDLE_IDENTIFIER}.nse"; PRODUCT_DISPLAY_NAME = "$(APP_DISPLAY_NAME)"; PRODUCT_NAME = NSE; @@ -7328,9 +7334,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = "$(MARKETING_VERSION)"; - OTHER_SWIFT_FLAGS = ( - "-DIS_MAIN_APP", - ); + OTHER_SWIFT_FLAGS = "-DIS_MAIN_APP"; PILLS_UT_TYPE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).pills"; PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(APP_NAME)"; @@ -7356,9 +7360,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = "$(MARKETING_VERSION)"; - OTHER_SWIFT_FLAGS = ( - "-DIS_MAIN_APP", - ); + OTHER_SWIFT_FLAGS = "-DIS_MAIN_APP"; PILLS_UT_TYPE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).pills"; PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(APP_NAME)"; @@ -7603,9 +7605,7 @@ "@executable_path/../../Frameworks", ); MARKETING_VERSION = "$(MARKETING_VERSION)"; - OTHER_SWIFT_FLAGS = ( - "-DIS_NSE", - ); + OTHER_SWIFT_FLAGS = "-DIS_NSE"; PRODUCT_BUNDLE_IDENTIFIER = "${BASE_BUNDLE_IDENTIFIER}.nse"; PRODUCT_DISPLAY_NAME = "$(APP_DISPLAY_NAME)"; PRODUCT_NAME = NSE; diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index fea01d5a7a..3724e95cab 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -226,6 +226,7 @@ "common_username" = "Username"; "common_verification_cancelled" = "Verification cancelled"; "common_verification_complete" = "Verification complete"; +"common_verified" = "Verified"; "common_verify_device" = "Verify device"; "common_video" = "Video"; "common_voice_message" = "Voice message"; @@ -341,6 +342,10 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -504,7 +509,7 @@ "screen_invites_empty_list" = "No Invites"; "screen_invites_invited_you" = "%1$@ (%2$@) invited you"; "screen_join_room_join_action" = "Join room"; -"screen_join_room_knock_action" = "Knock to join"; +"screen_join_room_knock_action" = "Send request to join"; "screen_join_room_space_not_supported_description" = "%1$@ does not support spaces yet. You can access spaces on web."; "screen_join_room_space_not_supported_title" = "Spaces are not supported yet"; "screen_join_room_subtitle_knock" = "Click the button below and a room administrator will be notified. You’ll be able to join the conversation once approved."; diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index 1c10cdbc56..5eabe89580 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -662,7 +662,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { via: via, clientProxy: userSession.clientProxy, mediaProvider: userSession.mediaProvider, - userIndicatorController: userIndicatorController)) + userIndicatorController: userIndicatorController, + appSettings: appSettings)) joinRoomScreenCoordinator = coordinator diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index a14b6da1e7..11dfb30e47 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -502,6 +502,8 @@ internal enum L10n { internal static var commonVerificationCancelled: String { return L10n.tr("Localizable", "common_verification_cancelled") } /// Verification complete internal static var commonVerificationComplete: String { return L10n.tr("Localizable", "common_verification_complete") } + /// Verified + internal static var commonVerified: String { return L10n.tr("Localizable", "common_verified") } /// Verify device internal static var commonVerifyDevice: String { return L10n.tr("Localizable", "common_verify_device") } /// Video @@ -1187,10 +1189,18 @@ internal enum L10n { internal static func screenInvitesInvitedYou(_ p1: Any, _ p2: Any) -> String { return L10n.tr("Localizable", "screen_invites_invited_you", String(describing: p1), String(describing: p2)) } + /// Cancel request + internal static var screenJoinRoomCancelKnockAction: String { return L10n.tr("Localizable", "screen_join_room_cancel_knock_action") } /// Join room internal static var screenJoinRoomJoinAction: String { return L10n.tr("Localizable", "screen_join_room_join_action") } - /// Knock to join + /// Send request to join internal static var screenJoinRoomKnockAction: String { return L10n.tr("Localizable", "screen_join_room_knock_action") } + /// Message (optional) + internal static var screenJoinRoomKnockMessageDescription: String { return L10n.tr("Localizable", "screen_join_room_knock_message_description") } + /// You will receive an invite to join the room if your request is accepted. + internal static var screenJoinRoomKnockSentDescription: String { return L10n.tr("Localizable", "screen_join_room_knock_sent_description") } + /// Request to join sent + internal static var screenJoinRoomKnockSentTitle: String { return L10n.tr("Localizable", "screen_join_room_knock_sent_title") } /// %1$@ does not support spaces yet. You can access spaces on web. internal static func screenJoinRoomSpaceNotSupportedDescription(_ p1: Any) -> String { return L10n.tr("Localizable", "screen_join_room_space_not_supported_description", String(describing: p1)) diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 1b472cd45a..039932ee7f 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -2835,6 +2835,146 @@ class ClientProxyMock: ClientProxyProtocol { return joinRoomAliasReturnValue } } + //MARK: - knockRoom + + var knockRoomMessageUnderlyingCallsCount = 0 + var knockRoomMessageCallsCount: Int { + get { + if Thread.isMainThread { + return knockRoomMessageUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = knockRoomMessageUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + knockRoomMessageUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + knockRoomMessageUnderlyingCallsCount = newValue + } + } + } + } + var knockRoomMessageCalled: Bool { + return knockRoomMessageCallsCount > 0 + } + var knockRoomMessageReceivedArguments: (roomID: String, message: String?)? + var knockRoomMessageReceivedInvocations: [(roomID: String, message: String?)] = [] + + var knockRoomMessageUnderlyingReturnValue: Result! + var knockRoomMessageReturnValue: Result! { + get { + if Thread.isMainThread { + return knockRoomMessageUnderlyingReturnValue + } else { + var returnValue: Result? = nil + DispatchQueue.main.sync { + returnValue = knockRoomMessageUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + knockRoomMessageUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + knockRoomMessageUnderlyingReturnValue = newValue + } + } + } + } + var knockRoomMessageClosure: ((String, String?) async -> Result)? + + func knockRoom(_ roomID: String, message: String?) async -> Result { + knockRoomMessageCallsCount += 1 + knockRoomMessageReceivedArguments = (roomID: roomID, message: message) + DispatchQueue.main.async { + self.knockRoomMessageReceivedInvocations.append((roomID: roomID, message: message)) + } + if let knockRoomMessageClosure = knockRoomMessageClosure { + return await knockRoomMessageClosure(roomID, message) + } else { + return knockRoomMessageReturnValue + } + } + //MARK: - knockRoomAlias + + var knockRoomAliasMessageUnderlyingCallsCount = 0 + var knockRoomAliasMessageCallsCount: Int { + get { + if Thread.isMainThread { + return knockRoomAliasMessageUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = knockRoomAliasMessageUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + knockRoomAliasMessageUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + knockRoomAliasMessageUnderlyingCallsCount = newValue + } + } + } + } + var knockRoomAliasMessageCalled: Bool { + return knockRoomAliasMessageCallsCount > 0 + } + var knockRoomAliasMessageReceivedArguments: (roomAlias: String, message: String?)? + var knockRoomAliasMessageReceivedInvocations: [(roomAlias: String, message: String?)] = [] + + var knockRoomAliasMessageUnderlyingReturnValue: Result! + var knockRoomAliasMessageReturnValue: Result! { + get { + if Thread.isMainThread { + return knockRoomAliasMessageUnderlyingReturnValue + } else { + var returnValue: Result? = nil + DispatchQueue.main.sync { + returnValue = knockRoomAliasMessageUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + knockRoomAliasMessageUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + knockRoomAliasMessageUnderlyingReturnValue = newValue + } + } + } + } + var knockRoomAliasMessageClosure: ((String, String?) async -> Result)? + + func knockRoomAlias(_ roomAlias: String, message: String?) async -> Result { + knockRoomAliasMessageCallsCount += 1 + knockRoomAliasMessageReceivedArguments = (roomAlias: roomAlias, message: message) + DispatchQueue.main.async { + self.knockRoomAliasMessageReceivedInvocations.append((roomAlias: roomAlias, message: message)) + } + if let knockRoomAliasMessageClosure = knockRoomAliasMessageClosure { + return await knockRoomAliasMessageClosure(roomAlias, message) + } else { + return knockRoomAliasMessageReturnValue + } + } //MARK: - uploadMedia var uploadMediaUnderlyingCallsCount = 0 @@ -9541,6 +9681,117 @@ class KeychainControllerMock: KeychainControllerProtocol { removePINCodeBiometricStateClosure?() } } +class KnockedRoomProxyMock: KnockedRoomProxyProtocol { + var id: String { + get { return underlyingId } + set(value) { underlyingId = value } + } + var underlyingId: String! + var canonicalAlias: String? + var ownUserID: String { + get { return underlyingOwnUserID } + set(value) { underlyingOwnUserID = value } + } + var underlyingOwnUserID: String! + var name: String? + var topic: String? + var avatar: RoomAvatar { + get { return underlyingAvatar } + set(value) { underlyingAvatar = value } + } + var underlyingAvatar: RoomAvatar! + var avatarURL: URL? + var isPublic: Bool { + get { return underlyingIsPublic } + set(value) { underlyingIsPublic = value } + } + var underlyingIsPublic: Bool! + var isDirect: Bool { + get { return underlyingIsDirect } + set(value) { underlyingIsDirect = value } + } + var underlyingIsDirect: Bool! + var isSpace: Bool { + get { return underlyingIsSpace } + set(value) { underlyingIsSpace = value } + } + var underlyingIsSpace: Bool! + var joinedMembersCount: Int { + get { return underlyingJoinedMembersCount } + set(value) { underlyingJoinedMembersCount = value } + } + var underlyingJoinedMembersCount: Int! + var activeMembersCount: Int { + get { return underlyingActiveMembersCount } + set(value) { underlyingActiveMembersCount = value } + } + var underlyingActiveMembersCount: Int! + + //MARK: - cancelKnock + + var cancelKnockUnderlyingCallsCount = 0 + var cancelKnockCallsCount: Int { + get { + if Thread.isMainThread { + return cancelKnockUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = cancelKnockUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + cancelKnockUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + cancelKnockUnderlyingCallsCount = newValue + } + } + } + } + var cancelKnockCalled: Bool { + return cancelKnockCallsCount > 0 + } + + var cancelKnockUnderlyingReturnValue: Result! + var cancelKnockReturnValue: Result! { + get { + if Thread.isMainThread { + return cancelKnockUnderlyingReturnValue + } else { + var returnValue: Result? = nil + DispatchQueue.main.sync { + returnValue = cancelKnockUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + cancelKnockUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + cancelKnockUnderlyingReturnValue = newValue + } + } + } + } + var cancelKnockClosure: (() async -> Result)? + + func cancelKnock() async -> Result { + cancelKnockCallsCount += 1 + if let cancelKnockClosure = cancelKnockClosure { + return await cancelKnockClosure() + } else { + return cancelKnockReturnValue + } + } +} class MediaLoaderMock: MediaLoaderProtocol { //MARK: - loadMediaContentForSource diff --git a/ElementX/Sources/Mocks/KnockedRoomProxyMock.swift b/ElementX/Sources/Mocks/KnockedRoomProxyMock.swift new file mode 100644 index 0000000000..610e975a57 --- /dev/null +++ b/ElementX/Sources/Mocks/KnockedRoomProxyMock.swift @@ -0,0 +1,29 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Combine +import Foundation + +@MainActor +struct KnockedRoomProxyMockConfiguration { + var id = UUID().uuidString + var name: String? + var avatarURL: URL? + var members: [RoomMemberProxyMock] = .allMembers +} + +extension KnockedRoomProxyMock { + @MainActor + convenience init(_ configuration: KnockedRoomProxyMockConfiguration) { + self.init() + id = configuration.id + name = configuration.name + avatarURL = configuration.avatarURL + avatar = .room(id: configuration.id, name: configuration.name, avatarURL: configuration.avatarURL) // Note: This doesn't replicate the real proxy logic. + activeMembersCount = configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count + } +} diff --git a/ElementX/Sources/Other/Extensions/String.swift b/ElementX/Sources/Other/Extensions/String.swift index 58caff4888..2e27561a2e 100644 --- a/ElementX/Sources/Other/Extensions/String.swift +++ b/ElementX/Sources/Other/Extensions/String.swift @@ -90,3 +90,10 @@ extension String { return result } } + +extension String { + /// detects if the string is empty or contains only whitespaces and newlines + var isBlank: Bool { + trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + } +} diff --git a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenCoordinator.swift b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenCoordinator.swift index e669b42049..fbe2e23c0d 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenCoordinator.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenCoordinator.swift @@ -14,6 +14,7 @@ struct JoinRoomScreenCoordinatorParameters { let clientProxy: ClientProxyProtocol let mediaProvider: MediaProviderProtocol let userIndicatorController: UserIndicatorControllerProtocol + let appSettings: AppSettings } enum JoinRoomScreenCoordinatorAction { @@ -34,6 +35,7 @@ final class JoinRoomScreenCoordinator: CoordinatorProtocol { init(parameters: JoinRoomScreenCoordinatorParameters) { viewModel = JoinRoomScreenViewModel(roomID: parameters.roomID, via: parameters.via, + appSettings: parameters.appSettings, clientProxy: parameters.clientProxy, mediaProvider: parameters.mediaProvider, userIndicatorController: parameters.userIndicatorController) diff --git a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenModels.swift b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenModels.swift index f2c5d4ac60..a0bf1d91c6 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenModels.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenModels.swift @@ -18,6 +18,7 @@ enum JoinRoomScreenInteractionMode { case invited case join case knock + case knocked } struct JoinRoomScreenRoomDetails { @@ -48,6 +49,7 @@ struct JoinRoomScreenViewState: BindableState { case .loading: nil case .unknown: L10n.screenJoinRoomSubtitleNoPreview case .invited, .join, .knock: roomDetails?.canonicalAlias + case .knocked: nil } } @@ -58,6 +60,7 @@ struct JoinRoomScreenViewState: BindableState { struct JoinRoomScreenViewStateBindings { var alertInfo: AlertInfo? + var knockMessage = "" } enum JoinRoomScreenAlertType { @@ -65,6 +68,7 @@ enum JoinRoomScreenAlertType { } enum JoinRoomScreenViewAction { + case cancelKnock case knock case join case acceptInvite diff --git a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift index da59f09bad..5088efa29f 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift @@ -13,7 +13,7 @@ typealias JoinRoomScreenViewModelType = StateStoreViewModel JoinRoomScreenViewModel { @@ -145,11 +214,18 @@ struct JoinRoomScreen_Previews: PreviewProvider, TestablePreview { (false, false, true, false) case .knock: (false, false, false, true) + case .knocked: + (false, false, false, false) } if mode == .unknown { clientProxy.roomPreviewForIdentifierViaReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) } else { + if mode == .knocked { + clientProxy.roomForIdentifierClosure = { _ in + .knocked(KnockedRoomProxyMock(.init())) + } + } clientProxy.roomPreviewForIdentifierViaReturnValue = .success(.init(roomID: "1", name: "The Three-Body Problem - 三体", canonicalAlias: "#3🌞problem:matrix.org", @@ -164,9 +240,11 @@ struct JoinRoomScreen_Previews: PreviewProvider, TestablePreview { canKnock: membership.canKnock)) } + ServiceLocator.shared.settings.knockingEnabled = true + return JoinRoomScreenViewModel(roomID: "1", via: [], - allowKnocking: true, + appSettings: ServiceLocator.shared.settings, clientProxy: clientProxy, mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController) diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 72a647283a..06972a1ec7 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -415,6 +415,30 @@ class ClientProxy: ClientProxyProtocol { } } + func knockRoom(_ roomID: String, message: String?) async -> Result { + do { + // TODO: It should also include a message but the API for is not available yet + let _ = try await client.knock(roomIdOrAlias: roomID) + await waitForRoomToSync(roomID: roomID, timeout: .seconds(30)) + return .success(()) + } catch { + MXLog.error("Failed knocking roomID: \(roomID) with error: \(error)") + return .failure(.sdkError(error)) + } + } + + func knockRoomAlias(_ roomAlias: String, message: String?) async -> Result { + do { + // TODO: It should also include a message but the API for is not available yet + let room = try await client.knock(roomIdOrAlias: roomAlias) + await waitForRoomToSync(roomID: room.id(), timeout: .seconds(30)) + return .success(()) + } catch { + MXLog.error("Failed knocking roomAlias: \(roomAlias) with error: \(error)") + return .failure(.sdkError(error)) + } + } + func uploadMedia(_ media: MediaInfo) async -> Result { guard let mimeType = media.mimeType else { MXLog.error("Failed uploading media, invalid mime type: \(media)") @@ -846,9 +870,17 @@ class ClientProxy: ClientProxyProtocol { let roomListItem = try roomListService.room(roomId: roomID) switch roomListItem.membership() { - case .invited, .knocked: + case .invited: return try .invited(InvitedRoomProxy(roomListItem: roomListItem, room: roomListItem.invitedRoom())) + case .knocked: + if appSettings.knockingEnabled { + return try .knocked(KnockedRoomProxy(roomListItem: roomListItem, + room: roomListItem.invitedRoom())) + } else { + return try .invited(InvitedRoomProxy(roomListItem: roomListItem, + room: roomListItem.invitedRoom())) + } case .joined: if roomListItem.isTimelineInitialized() == false { try await roomListItem.initTimeline(eventTypeFilter: eventFilters, internalIdPrefix: nil) diff --git a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift index e8160f2412..aa7663a533 100644 --- a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift +++ b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift @@ -145,6 +145,10 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol { func joinRoomAlias(_ roomAlias: String) async -> Result + func knockRoom(_ roomID: String, message: String?) async -> Result + + func knockRoomAlias(_ roomAlias: String, message: String?) async -> Result + func uploadMedia(_ media: MediaInfo) async -> Result func roomForIdentifier(_ identifier: String) async -> RoomProxyType? diff --git a/ElementX/Sources/Services/Room/KnockedRoomProxy.swift b/ElementX/Sources/Services/Room/KnockedRoomProxy.swift new file mode 100644 index 0000000000..942baa0412 --- /dev/null +++ b/ElementX/Sources/Services/Room/KnockedRoomProxy.swift @@ -0,0 +1,82 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Foundation +import MatrixRustSDK +import UIKit + +class KnockedRoomProxy: KnockedRoomProxyProtocol { + private let roomListItem: RoomListItemProtocol + private let room: RoomProtocol + + // A room identifier is constant and lazy stops it from being fetched + // multiple times over FFI + lazy var id: String = room.id() + + var canonicalAlias: String? { + room.canonicalAlias() + } + + var ownUserID: String { + room.ownUserId() + } + + var name: String? { + roomListItem.displayName() + } + + var topic: String? { + room.topic() + } + + var avatarURL: URL? { + roomListItem.avatarUrl().flatMap(URL.init(string:)) + } + + var avatar: RoomAvatar { + if isDirect, avatarURL == nil { + let heroes = room.heroes() + + if heroes.count == 1 { + return .heroes(heroes.map(UserProfileProxy.init)) + } + } + + return .room(id: id, name: name, avatarURL: avatarURL) + } + + var isDirect: Bool { + room.isDirect() + } + + var isPublic: Bool { + room.isPublic() + } + + var isSpace: Bool { + room.isSpace() + } + + var joinedMembersCount: Int { + Int(room.joinedMembersCount()) + } + + var activeMembersCount: Int { + Int(room.activeMembersCount()) + } + + init(roomListItem: RoomListItemProtocol, + room: RoomProtocol) { + self.roomListItem = roomListItem + self.room = room + } + + func cancelKnock() async -> Result { + // TODO: Implement this once the API is available + .failure(.invalidURL) + } +} diff --git a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift index 552e0484d6..46cc11f080 100644 --- a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift @@ -21,6 +21,7 @@ enum RoomProxyError: Error { enum RoomProxyType { case joined(JoinedRoomProxyProtocol) case invited(InvitedRoomProxyProtocol) + case knocked(KnockedRoomProxyProtocol) case left } @@ -56,6 +57,11 @@ protocol InvitedRoomProxyProtocol: RoomProxyProtocol { func acceptInvitation() async -> Result } +// sourcery: AutoMockable +protocol KnockedRoomProxyProtocol: RoomProxyProtocol { + func cancelKnock() async -> Result +} + enum JoinedRoomProxyAction: Equatable { case roomInfoUpdate } diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knock.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knock.png index 1789652cdb..3b61214cdc 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knock.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knock.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b9bb89feeb7a80c3bdd110bf69eb2efb08b009ebcb30825a24c2343a537280df -size 1938161 +oid sha256:adb3e3a08681e2febea9cd377862eededc6b576bfe06c753bf075eb886f55f9f +size 1788568 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knocked.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knocked.png new file mode 100644 index 0000000000..16efb5ef59 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knocked.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79f1c8ea20c6791a7d25d8fc18f45d1d0fef989b8fa0316c5b312bfc9dd4c271 +size 1972382 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knock.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knock.png index 60860bc7e2..d34ffa65c4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knock.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knock.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd7969bf12e64467689fe589abd6f193425b114778f0cfac04ec6abf78000b98 -size 1938771 +oid sha256:338b8be69ace718a2796101e2ccfbe93dca412e299279a6078b785fec10e7e4b +size 1797161 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knocked.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knocked.png new file mode 100644 index 0000000000..65181a2704 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knocked.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d159ef72270668acb4ffe1443954086a895e630d5809ff51a65b9302ada8ca89 +size 1988527 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knock.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knock.png index 49e1c4a792..f0f6bd47a8 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knock.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knock.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fb08acf5df0a46b166d2e0b4b4ebcdf399c5ce0acf6b6b728124b817aae8d0c -size 791872 +oid sha256:0be47f8b5c89c0d03e4713e32686c457452851ccc246bf4588f5cb5f82fdbabc +size 701611 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knocked.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knocked.png new file mode 100644 index 0000000000..4e8d7e6c1c --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knocked.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e0a6de1d32f7d2845bf66ae655ac2b7404083a6d3478f96e1eea29f0b102e51 +size 811030 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knock.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knock.png index c2b2930b16..999c04fe9b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knock.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knock.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9685beb39e3ca4a7509b5438ec4733fce1019d8aae6fb12d2a1a87214a971b00 -size 793600 +oid sha256:471ae5bf707a3f9e91b7f8cea098287bb21d618297d027a72c105575b5049ff8 +size 709284 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knocked.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knocked.png new file mode 100644 index 0000000000..daca3b20bf --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knocked.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e8581d10df35158cd98cc36e23f1ca1ad38659732699bd7073f92aa4331e847 +size 828204 diff --git a/UnitTests/Sources/JoinRoomScreenViewModelTests.swift b/UnitTests/Sources/JoinRoomScreenViewModelTests.swift index c0a0984cee..42b1b6a526 100644 --- a/UnitTests/Sources/JoinRoomScreenViewModelTests.swift +++ b/UnitTests/Sources/JoinRoomScreenViewModelTests.swift @@ -16,6 +16,11 @@ class JoinRoomScreenViewModelTests: XCTestCase { var context: JoinRoomScreenViewModelType.Context { viewModel.context } + + override func tearDown() { + viewModel = nil + AppSettings.resetAllSettings() + } func testInteraction() async throws { setupViewModel() @@ -43,7 +48,15 @@ class JoinRoomScreenViewModelTests: XCTestCase { XCTAssertEqual(viewModel.context.alertInfo?.id, .declineInvite) } - private func setupViewModel(throwing: Bool = false) { + func testKnockedState() async throws { + setupViewModel(knocked: true) + + try await deferFulfillment(viewModel.context.$viewState) { state in + state.mode == .knocked + }.fulfill() + } + + private func setupViewModel(throwing: Bool = false, knocked: Bool = false) { let clientProxy = ClientProxyMock(.init()) clientProxy.joinRoomViaReturnValue = throwing ? .failure(.sdkError(ClientProxyMockError.generic)) : .success(()) @@ -60,8 +73,17 @@ class JoinRoomScreenViewModelTests: XCTestCase { isPublic: false, canKnock: false)) + if knocked { + clientProxy.roomForIdentifierClosure = { _ in + .knocked(KnockedRoomProxyMock(.init())) + } + } + + ServiceLocator.shared.settings.knockingEnabled = true + viewModel = JoinRoomScreenViewModel(roomID: "1", via: [], + appSettings: ServiceLocator.shared.settings, clientProxy: clientProxy, mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: ServiceLocator.shared.userIndicatorController) From dbe64856900dd2a37137b5fabc76627cf91130dc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 04:48:23 +0000 Subject: [PATCH 066/114] Update dependency fastlane to v2.225.0 --- Gemfile.lock | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8e22eaab5e..f196f469c2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -16,17 +16,17 @@ GEM artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.3.0) - aws-partitions (1.984.0) - aws-sdk-core (3.209.1) + aws-partitions (1.992.0) + aws-sdk-core (3.210.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) + aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.94.0) - aws-sdk-core (~> 3, >= 3.207.0) + aws-sdk-kms (1.95.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.167.0) - aws-sdk-core (~> 3, >= 3.207.0) + aws-sdk-s3 (1.169.0) + aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) aws-sigv4 (1.10.0) @@ -74,7 +74,7 @@ GEM faraday_middleware (1.2.1) faraday (~> 1.0) fastimage (2.3.1) - fastlane (2.224.0) + fastlane (2.225.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -90,6 +90,7 @@ GEM faraday-cookie_jar (~> 0.0.6) faraday_middleware (~> 1.0) fastimage (>= 2.1.0, < 3.0.0) + fastlane-sirp (>= 1.0.0) gh_inspector (>= 1.1.2, < 2.0.0) google-apis-androidpublisher_v3 (~> 0.3) google-apis-playcustomapp_v1 (~> 0.1) @@ -123,6 +124,8 @@ GEM fastlane-plugin-xcconfig (2.1.0) fastlane-plugin-xcodegen (1.1.0) fastlane-plugin-brew (~> 0.1.1) + fastlane-sirp (1.0.0) + sysrandom (~> 1.0) gh_inspector (1.1.3) google-apis-androidpublisher_v3 (0.54.0) google-apis-core (>= 0.11.0, < 2.a) @@ -209,6 +212,7 @@ GEM simctl (1.6.10) CFPropertyList naturally + sysrandom (1.0.5) terminal-notifier (2.0.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) From 4ad0a40898484f4c9cfe8537f6305413a37be0c4 Mon Sep 17 00:00:00 2001 From: Velin92 <34335419+Velin92@users.noreply.github.com> Date: Mon, 21 Oct 2024 00:01:37 +0000 Subject: [PATCH 067/114] Translations update --- .../be.lproj/Localizable.strings | 46 ++-- .../bg.lproj/Localizable.strings | 22 +- .../cs.lproj/Localizable.strings | 20 ++ .../de.lproj/Localizable.strings | 20 ++ .../el.lproj/Localizable.strings | 34 ++- .../en.lproj/Localizable.strings | 13 ++ .../es.lproj/Localizable.strings | 22 +- .../et.lproj/Localizable.strings | 20 ++ .../fa.lproj/Localizable.strings | 20 ++ .../fr.lproj/Localizable.strings | 20 ++ .../hu.lproj/Localizable.strings | 20 ++ .../id.lproj/Localizable.strings | 20 ++ .../it.lproj/Localizable.strings | 20 ++ .../ka.lproj/Localizable.strings | 22 +- .../nl.lproj/Localizable.strings | 20 ++ .../pl.lproj/Localizable.strings | 20 ++ .../pt-BR.lproj/Localizable.strings | 22 +- .../pt.lproj/Localizable.strings | 48 ++-- .../ro.lproj/Localizable.strings | 20 ++ .../ru.lproj/Localizable.strings | 208 ++++++++++-------- .../ru.lproj/Localizable.stringsdict | 18 +- .../sk.lproj/Localizable.strings | 20 ++ .../sv.lproj/Localizable.strings | 20 ++ .../uk.lproj/Localizable.strings | 20 ++ .../uz.lproj/Localizable.strings | 22 +- .../zh-Hans.lproj/Localizable.strings | 20 ++ .../zh-Hant-TW.lproj/Localizable.strings | 22 +- ElementX/Sources/Generated/Strings.swift | 28 +++ 28 files changed, 684 insertions(+), 143 deletions(-) diff --git a/ElementX/Resources/Localizations/be.lproj/Localizable.strings b/ElementX/Resources/Localizations/be.lproj/Localizable.strings index b12ba3154e..deaf7b6ca1 100644 --- a/ElementX/Resources/Localizations/be.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/be.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Забылі пароль?"; "action_forward" = "Пераслаць"; "action_go_back" = "Вярнуцца"; +"action_ignore" = "Ignore"; "action_invite" = "Запрасіць"; "action_invite_friends" = "Запрасіць карыстальнікаў"; "action_invite_friends_to_app" = "Запрасіць карыстальнікаў у %1$@"; @@ -94,7 +95,7 @@ "action_send_message" = "Адправіць паведамленне"; "action_share" = "Падзяліцца"; "action_share_link" = "Абагуліць спасылку"; -"action_show" = "Show"; +"action_show" = "Паказаць"; "action_sign_in_again" = "Увайдзіце яшчэ раз"; "action_signout" = "Выйсці"; "action_signout_anyway" = "Усё роўна выйсці"; @@ -133,6 +134,7 @@ "common_dark" = "Цёмная"; "common_decryption_error" = "Памылка расшыфроўкі"; "common_developer_options" = "Параметры распрацоўшчыка"; +"common_device_id" = "Device ID"; "common_direct_chat" = "Прамы чат"; "common_edited_suffix" = "(Адрэдагавана)"; "common_editing" = "Рэдагаванне"; @@ -226,12 +228,14 @@ "common_username" = "Імя карыстальніка"; "common_verification_cancelled" = "Праверка адменена"; "common_verification_complete" = "Праверка завершана"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "Праверце прыладу"; "common_video" = "Відэа"; "common_voice_message" = "Галасавое паведамленне"; "common_waiting" = "Чакаем…"; "common_waiting_for_decryption_key" = "Чакаю гэта паведамленне"; -"common.copied_to_clipboard" = "Copied to clipboard"; +"common.copied_to_clipboard" = "Скапіравана ў буфер абмену"; "common.do_not_show_this_again" = "Не паказваць гэта зноў"; "common.open_source_licenses" = "Ліцэнзіі з адкрытым зыходным кодам"; "common.pinned" = "Замацаваны"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Увядзіце ключ аднаўлення"; "crash_detection_dialog_content" = "Пры апошнім выкарыстанні %1$@ адбыўся збой. Хочаце падзяліцца справаздачай аб збоі?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Каб дазволіць праграме выкарыстоўваць камеру, дайце дазвол у наладах сістэмы."; "dialog_permission_generic" = "Калі ласка, дайце дазвол у наладах сістэмы."; "dialog_permission_location_description_ios" = "Дайце доступ у Наладах -> Месца знаходжання."; @@ -335,10 +341,17 @@ "screen_advanced_settings_element_call_base_url_description" = "Усталюйце карыстальніцкі асноўны URL для Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Адрас пазначаны няправільна, пераканайцеся, што вы ўказалі пратакол (http/https) і правільны адрас."; "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"; +"screen_create_room_access_section_anyone_option_title" = "Хто заўгодна"; +"screen_create_room_access_section_header" = "Доступ у пакой"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; -"screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_create_room_access_section_knocking_option_title" = "Папрасіце далучыцца"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Націсніце на паведамленне і абярыце «%1$@ », каб уключыць сюды."; "screen_pinned_timeline_empty_state_headline" = "Замацуеце важныя паведамленні, каб іх можна было лёгка знайсці"; "screen_reset_encryption_password_error" = "Адбылася невядомая памылка. Калі ласка, праверце правільнасць пароля вашага ўліковага запісу і паўтарыце спробу."; @@ -462,15 +475,15 @@ "screen_create_room_public_option_description" = "Паведамленні не зашыфраваны, і кожны можа іх прачытаць. Вы можаце ўключыць шыфраванне пазней."; "screen_create_room_public_option_title" = "Публічны пакой (для ўсіх)"; "screen_create_room_topic_label" = "Тэма (неабавязкова)"; -"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"; -"screen_deactivate_account_delete_all_messages_notice" = "Warning: Future users may see incomplete conversations."; +"screen_deactivate_account_confirmation_dialog_content" = "Калі ласка, пацвердзіце, што вы хочаце дэактываваць свой уліковы запіс. Гэта дзеянне нельга адмяніць."; +"screen_deactivate_account_delete_all_messages" = "Выдаліць усе мае паведамленні"; +"screen_deactivate_account_delete_all_messages_notice" = "Увага: будучыя карыстальнікі могуць бачыць няпоўныя размовы."; "screen_deactivate_account_description" = "Deactivating your account is %1$@, it will:"; -"screen_deactivate_account_description_bold_part" = "irreversible"; +"screen_deactivate_account_description_bold_part" = "незваротны"; "screen_deactivate_account_list_item_1" = "%1$@ your account (you can't log back in, and your ID can't be reused)."; -"screen_deactivate_account_list_item_1_bold_part" = "Permanently disable"; -"screen_deactivate_account_list_item_2" = "Remove you from all chat rooms."; -"screen_deactivate_account_list_item_3" = "Delete your account information from our identity server."; +"screen_deactivate_account_list_item_1_bold_part" = "Назаўсёды адключыць"; +"screen_deactivate_account_list_item_2" = "Выдаліць вас з усіх чатаў."; +"screen_deactivate_account_list_item_3" = "Выдаліце інфармацыю аб сваім уліковым запісе з нашага сервера ідэнтыфікацыі."; "screen_deactivate_account_list_item_4" = "Your messages will still be visible to registered users but won’t be available to new or unregistered users if you choose to delete them."; "screen_deactivate_account_title" = "Дэактываваць уліковы запіс"; "screen_edit_poll_delete_confirmation" = "Вы ўпэўнены, што хочаце выдаліць гэтае апытанне?"; @@ -593,7 +606,7 @@ "screen_qr_code_login_initial_state_item_3" = "Выберыце %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Звязаць новую прыладу”"; "screen_qr_code_login_initial_state_item_4" = "Адсканіруйце QR-код з дапамогай гэтай прылады"; -"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; +"screen_qr_code_login_initial_state_subtitle" = "Даступна толькі ў тым выпадку, калі ваш правайдар уліковага запісу гэта падтрымлівае."; "screen_qr_code_login_initial_state_title" = "Адкрыйце %1$@ на іншай прыладзе, каб атрымаць QR-код"; "screen_qr_code_login_invalid_scan_state_description" = "Выкарыстоўвайце QR-код, паказаны на іншай прыладзе."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Няправільны QR-код"; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Разблакіраваць"; "screen_room_member_details_unblock_alert_description" = "Вы зноў зможаце ўбачыць усе паведамленні."; "screen_room_member_details_unblock_user" = "Разблакіраваць карыстальніка"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Заблакіраваць"; "screen_room_member_list_ban_member_confirmation_description" = "Яны не змогуць зноў далучыцца да гэтага пакоя, калі іх запросяць."; "screen_room_member_list_ban_member_confirmation_title" = "Вы ўпэўнены, што хочаце заблакіраваць гэтага карыстальніка?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Параўнайце лічбы"; "screen_session_verification_complete_subtitle" = "Ваш новы сеанс пацверджаны. Ён мае доступ да вашых зашыфраваных паведамленняў, і іншыя карыстальнікі будуць лічыць яго давераным."; "screen_session_verification_enter_recovery_key" = "Увядзіце ключ аднаўлення"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Дакажыце, што гэта вы, каб атрымаць доступ да вашай зашыфраванай гісторыі паведамленняў."; "screen_session_verification_open_existing_session_title" = "Адкрыйце існуючы сеанс"; "screen_session_verification_positive_button_canceled" = "Паўтарыце праверку"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Чаканне супадзення"; "screen_session_verification_ready_subtitle" = "Параўнайце ўнікальны набор эмодзі."; "screen_session_verification_request_accepted_subtitle" = "Параўнайце ўнікальныя эмодзі, пераканаўшыся, што яны размешчаны ў тым жа парадку."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Яны не супадаюць"; "screen_session_verification_they_match" = "Яны супадаюць"; "screen_session_verification_waiting_to_accept_subtitle" = "Для працягу працы прыміце запыт на запуск працэсу праверкі ў іншым сеансе."; diff --git a/ElementX/Resources/Localizations/bg.lproj/Localizable.strings b/ElementX/Resources/Localizations/bg.lproj/Localizable.strings index 7f1f532607..35b3aa9a49 100644 --- a/ElementX/Resources/Localizations/bg.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/bg.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Забравена парола?"; "action_forward" = "Препращане"; "action_go_back" = "Go back"; +"action_ignore" = "Ignore"; "action_invite" = "Поканване"; "action_invite_friends" = "Поканване на хора"; "action_invite_friends_to_app" = "Поканване на хора в %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Тъмен"; "common_decryption_error" = "Грешка при разшифроване"; "common_developer_options" = "Опции за разработчици"; +"common_device_id" = "Device ID"; "common_direct_chat" = "Директен чат"; "common_edited_suffix" = "(редактирано)"; "common_editing" = "Редактиране"; @@ -226,6 +228,8 @@ "common_username" = "Потребителско име"; "common_verification_cancelled" = "Потвърждаването е отменено"; "common_verification_complete" = "Потвърждаването е завършено"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "Потвърждаване на устройството"; "common_video" = "Видео"; "common_voice_message" = "Гласово съобщение"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Потвърдете ключа си за възстановяване"; "crash_detection_dialog_content" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "In order to let the application use the camera, please grant the permission in the system settings."; "dialog_permission_generic" = "Please grant the permission in the system settings."; "dialog_permission_location_description_ios" = "Grant access in Settings -> Location."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -502,7 +515,7 @@ "screen_invites_empty_list" = "Няма покани"; "screen_invites_invited_you" = "%1$@ (%2$@) ви покани"; "screen_join_room_join_action" = "Join room"; -"screen_join_room_knock_action" = "Knock to join"; +"screen_join_room_knock_action" = "Send request to join"; "screen_join_room_space_not_supported_description" = "%1$@ does not support spaces yet. You can access spaces on web."; "screen_join_room_space_not_supported_title" = "Spaces are not supported yet"; "screen_join_room_subtitle_knock" = "Click the button below and a room administrator will be notified. You’ll be able to join the conversation once approved."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Отблокиране"; "screen_room_member_details_unblock_alert_description" = "You'll be able to see all messages from them again."; "screen_room_member_details_unblock_user" = "Отблокиране на потребителя"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Ban"; "screen_room_member_list_ban_member_confirmation_description" = "They won’t be able to join this room again if invited."; "screen_room_member_list_ban_member_confirmation_title" = "Are you sure you want to ban this member?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Compare numbers"; "screen_session_verification_complete_subtitle" = "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted."; "screen_session_verification_enter_recovery_key" = "Въвеждане на ключ за възстановяване"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Докажете, че сте вие, за да получите достъп до хронологията на шифрованите си съобщения."; "screen_session_verification_open_existing_session_title" = "Отворете съществуваща сесия"; "screen_session_verification_positive_button_canceled" = "Повторен опит за потвърждаване"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "В очакване на съвпадение"; "screen_session_verification_ready_subtitle" = "Сравнете уникален набор от емоджита."; "screen_session_verification_request_accepted_subtitle" = "Compare the unique emoji, ensuring they appear in the same order."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Те не съвпадат"; "screen_session_verification_they_match" = "Те съвпадат"; "screen_session_verification_waiting_to_accept_subtitle" = "Приемете заявката, за да започнете процеса на потвърждаване в другата си сесия, за да продължите."; diff --git a/ElementX/Resources/Localizations/cs.lproj/Localizable.strings b/ElementX/Resources/Localizations/cs.lproj/Localizable.strings index 47ff860bb8..f6b8bd68a8 100644 --- a/ElementX/Resources/Localizations/cs.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/cs.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Zapomněli jste heslo?"; "action_forward" = "Přeposlat"; "action_go_back" = "Přejít zpět"; +"action_ignore" = "Ignorovat"; "action_invite" = "Pozvat"; "action_invite_friends" = "Pozvat přátele"; "action_invite_friends_to_app" = "Pozvat přátele do %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Tmavé"; "common_decryption_error" = "Chyba dešifrování"; "common_developer_options" = "Možnosti pro vývojáře"; +"common_device_id" = "ID zařízení"; "common_direct_chat" = "Přímý chat"; "common_edited_suffix" = "(upraveno)"; "common_editing" = "Úpravy"; @@ -226,6 +228,8 @@ "common_username" = "Uživatelské jméno"; "common_verification_cancelled" = "Ověření zrušeno"; "common_verification_complete" = "Ověření dokončeno"; +"common_verification_failed" = "Ověření se nezdařilo"; +"common_verified" = "Ověřeno"; "common_verify_device" = "Ověřit zařízení"; "common_video" = "Video"; "common_voice_message" = "Hlasová zpráva"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Potvrďte klíč pro obnovení"; "crash_detection_dialog_content" = "%1$@ havaroval při posledním použití. Chcete se s námi podělit o zprávu o selhání?"; "crypto_identity_change_pin_violation" = "Zdá se, že se identita %1$@ změnila. %2$@"; +"crypto_identity_change_pin_violation_new" = "Zdá se, že identita %1$@ %2$@ se změnila. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Aby mohla aplikace používat fotoaparát, udělte prosím oprávnění v nastavení systému."; "dialog_permission_generic" = "Udělte prosím oprávnění v nastavení systému."; "dialog_permission_location_description_ios" = "Udělte přístup v Nastavení -> Poloha."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Přístup do místnosti"; "screen_create_room_access_section_knocking_option_description" = "Kdokoli může požádat o vstup do místnosti, ale správce nebo moderátor bude muset žádost přijmout"; "screen_create_room_access_section_knocking_option_title" = "Požádat o připojení"; +"screen_join_room_cancel_knock_action" = "Zrušit žádost"; +"screen_join_room_cancel_knock_alert_confirmation" = "Ano, zrušit"; +"screen_join_room_cancel_knock_alert_description" = "Opravdu chcete zrušit svou žádost o vstup do této místnosti?"; +"screen_join_room_cancel_knock_alert_title" = "Zrušit žádost o vstup"; +"screen_join_room_knock_message_description" = "Zpráva (nepovinné)"; +"screen_join_room_knock_sent_description" = "Pokud bude váš požadavek přijat, obdržíte pozvánku na vstup do místnosti."; +"screen_join_room_knock_sent_title" = "Žádost o vstup odeslána"; "screen_pinned_timeline_empty_state_description" = "Přidržte zprávu a vyberte „%1$@“, kterou chcete zahrnout sem."; "screen_pinned_timeline_empty_state_headline" = "Připněte důležité zprávy, aby je bylo možné snadno najít"; "screen_reset_encryption_password_error" = "Došlo k neznámé chybě. Zkontrolujte, zda je heslo k účtu správné a zkuste to znovu."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Odblokovat"; "screen_room_member_details_unblock_alert_description" = "Znovu uvidíte všechny zprávy od nich."; "screen_room_member_details_unblock_user" = "Odblokovat uživatele"; +"screen_room_member_details_verify_button_subtitle" = "K ověření tohoto uživatele použijte webovou aplikaci."; +"screen_room_member_details_verify_button_title" = "Ověřit %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Vykázat"; "screen_room_member_list_ban_member_confirmation_description" = "Nebudou se moci znovu připojit k této místnosti, pokud budou pozváni."; "screen_room_member_list_ban_member_confirmation_title" = "Jste si jisti, že chcete vykázat tohoto člena?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Porovnejte čísla"; "screen_session_verification_complete_subtitle" = "Vaše nová relace je nyní ověřena. Má přístup k vašim zašifrovaným zprávám a ostatní uživatelé ji uvidí jako důvěryhodnou."; "screen_session_verification_enter_recovery_key" = "Zadejte klíč pro obnovení"; +"screen_session_verification_failed_subtitle" = "Buď vypršel časový limit požadavku, požadavek byl zamítnut, nebo došlo k nesouladu ověření."; "screen_session_verification_open_existing_session_subtitle" = "Pro přístup k historii zašifrovaných zpráv prokažte, že jste to vy."; "screen_session_verification_open_existing_session_title" = "Otevřete existující relaci"; "screen_session_verification_positive_button_canceled" = "Opakovat ověření"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Čekání na shodu"; "screen_session_verification_ready_subtitle" = "Porovnejte jedinečnou sadu emotikonů."; "screen_session_verification_request_accepted_subtitle" = "Porovnejte jedinečné emotikony a ujistěte se, že jsou zobrazeny ve stejném pořadí."; +"screen_session_verification_request_details_timestamp" = "Přihlášen"; +"screen_session_verification_request_footer" = "Pokračujte, pouze pokud jste toto ověření zahájili."; +"screen_session_verification_request_subtitle" = "Ověřte druhé zařízení, aby byla vaše historie zpráv zabezpečená."; +"screen_session_verification_request_title" = "Požadováno ověření"; "screen_session_verification_they_dont_match" = "Neshodují se"; "screen_session_verification_they_match" = "Shodují se"; "screen_session_verification_waiting_to_accept_subtitle" = "Pro pokračování přijměte požadavek na zahájení ověření v jiné relaci."; diff --git a/ElementX/Resources/Localizations/de.lproj/Localizable.strings b/ElementX/Resources/Localizations/de.lproj/Localizable.strings index 8c197aa41b..e3f1d512ca 100644 --- a/ElementX/Resources/Localizations/de.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/de.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Passwort vergessen?"; "action_forward" = "Weiterleiten"; "action_go_back" = "Zurück"; +"action_ignore" = "Ignore"; "action_invite" = "Einladen"; "action_invite_friends" = "Personen einladen"; "action_invite_friends_to_app" = "Zu %1$@ einladen"; @@ -133,6 +134,7 @@ "common_dark" = "Dunkel"; "common_decryption_error" = "Dekodierungsfehler"; "common_developer_options" = "Entwickleroptionen"; +"common_device_id" = "Device ID"; "common_direct_chat" = "Direktnachricht"; "common_edited_suffix" = "(bearbeitet)"; "common_editing" = "Bearbeitung"; @@ -226,6 +228,8 @@ "common_username" = "Benutzername"; "common_verification_cancelled" = "Verifizierung abgebrochen"; "common_verification_complete" = "Verifizierung abgeschlossen"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "Gerät verifizieren"; "common_video" = "Video"; "common_voice_message" = "Sprachnachricht"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Wiederherstellungsschlüssel bestätigen."; "crash_detection_dialog_content" = "%1$@ ist bei der letzten Nutzung abgestürzt. Möchtest du einen Absturzbericht mit uns teilen?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Damit die Anwendung die Kamera verwenden kann, erteile bitte die Erlaubnis in den Systemeinstellungen."; "dialog_permission_generic" = "Bitte erteile die Erlaubnis in den Systemeinstellungen."; "dialog_permission_location_description_ios" = "Erlaube den Zugriff unter Einstellungen -> Standort."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Drücke auf eine Nachricht und wähle “%1$@”, um sie hier einzufügen."; "screen_pinned_timeline_empty_state_headline" = "Fixiere wichtige Nachrichten, so dass sie leicht gefunden werden können"; "screen_reset_encryption_password_error" = "Es ist ein unbekannter Fehler aufgetreten. Bitte überprüfe das Passwort deines Kontos und versuche es erneut."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Blockierung aufheben"; "screen_room_member_details_unblock_alert_description" = "Der Nutzer kann dir wieder Nachrichten senden & alle Nachrichten des Nutzers werden wieder angezeigt."; "screen_room_member_details_unblock_user" = "Blockierung aufheben"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Sperren"; "screen_room_member_list_ban_member_confirmation_description" = "Sie können dem Raum nicht mehr beitreten, selbst wenn sie eingeladen werden."; "screen_room_member_list_ban_member_confirmation_title" = "Bist du sicher, dass du dieses Mitglied sperren möchtest?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Vergleiche die Zahlen"; "screen_session_verification_complete_subtitle" = "Deine neue Session ist nun verifiziert. Sie hat Zugriff auf deine verschlüsselten Nachrichten und wird von anderen Benutzern als vertrauenswürdig eingestuft."; "screen_session_verification_enter_recovery_key" = "Wiederherstellungsschlüssel eingeben"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Beweise deine Identität, um auf deinen verschlüsselten Nachrichtenverlauf zuzugreifen."; "screen_session_verification_open_existing_session_title" = "Öffne eine bestehende Session"; "screen_session_verification_positive_button_canceled" = "Verifizierung wiederholen"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Warten auf eine Übereinstimmung"; "screen_session_verification_ready_subtitle" = "Vergleiche eine spezielle Reihe von Emojis."; "screen_session_verification_request_accepted_subtitle" = "Vergleiche die einzelnen Emojis und stelle sicher, dass sie in der gleichen Reihenfolge erscheinen."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Sie stimmen nicht überein"; "screen_session_verification_they_match" = "Sie stimmen überein"; "screen_session_verification_waiting_to_accept_subtitle" = "Akzeptiere die Anfrage, um den Verifizierungsprozess in deiner anderen Session zu starten, um fortzufahren."; diff --git a/ElementX/Resources/Localizations/el.lproj/Localizable.strings b/ElementX/Resources/Localizations/el.lproj/Localizable.strings index e3eb06a1e1..dfd8b111d2 100644 --- a/ElementX/Resources/Localizations/el.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/el.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Ξέχασες τον κωδικό πρόσβασης;"; "action_forward" = "Προώθηση"; "action_go_back" = "Πήγαινε πίσω"; +"action_ignore" = "Ignore"; "action_invite" = "Πρόσκληση"; "action_invite_friends" = "Πρόσκληση ατόμων"; "action_invite_friends_to_app" = "Πρόσκληση ατόμων στο %1$@"; @@ -110,7 +111,7 @@ "action_view_in_timeline" = "Προβολή στο χρονοδιάγραμμα"; "action_view_source" = "Προβολή πηγής"; "action_yes" = "Ναι"; -"banner_migrate_to_native_sliding_sync_action" = "Αποσύνδεση & Αναβάθμιση"; +"banner_migrate_to_native_sliding_sync_action" = "Αποσύνδεση & Αναβάθμιση"; "banner_migrate_to_native_sliding_sync_description" = "Ο διακομιστής σου υποστηρίζει τώρα ένα νέο, ταχύτερο πρωτόκολλο. Αποσυνδέσου και συνδέσου ξανά για αναβάθμιση τώρα. Κάνοντας αυτό τώρα θα σε βοηθήσει να αποφύγεις μια αναγκαστική αποσύνδεση όταν το παλιό πρωτόκολλο καταργηθεί αργότερα."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Ο οικιακός διακομιστής σου δεν υποστηρίζει πλέον το παλιό πρωτόκολλο. Αποσυνδέσου και συνδέσου ξανά για να συνεχίσεις να χρησιμοποιείς την εφαρμογή."; "banner_migrate_to_native_sliding_sync_title" = "Διαθέσιμη αναβάθμιση"; @@ -133,6 +134,7 @@ "common_dark" = "Σκοτεινό"; "common_decryption_error" = "Σφάλμα αποκρυπτογράφησης"; "common_developer_options" = "Επιλογές προγραμματιστή"; +"common_device_id" = "Device ID"; "common_direct_chat" = "Άμεση συνομιλία"; "common_edited_suffix" = "(επεξεργάστηκε)"; "common_editing" = "Επεξεργάζεται"; @@ -226,6 +228,8 @@ "common_username" = "Όνομα χρήστη"; "common_verification_cancelled" = "Η επαλήθευση ακυρώθηκε"; "common_verification_complete" = "Η επαλήθευση ολοκληρώθηκε"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Επαληθεύτηκε"; "common_verify_device" = "Επαλήθευση συσκευής"; "common_video" = "Βίντεο"; "common_voice_message" = "Φωνητικό μήνυμα"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Εισήγαγε το κλειδί ανάκτησης"; "crash_detection_dialog_content" = "Το %1$@ διακόπηκε την τελευταία φορά που χρησιμοποιήθηκε. Θα 'θελες να μοιραστείς μια αναφορά σφάλματος μαζί μας;"; "crypto_identity_change_pin_violation" = "Η ταυτότητα του χρήστη %1$@ φαίνεται να έχει αλλάξει. %2$@"; +"crypto_identity_change_pin_violation_new" = "Η ταυτότητα του %1$@ %2$@ φαίνεται να έχει αλλάξει. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Για να επιτρέψεις στην εφαρμογή να χρησιμοποιεί την κάμερα, παραχώρησε την άδεια στις ρυθμίσεις συστήματος."; "dialog_permission_generic" = "Παρακαλώ παραχώρησε την άδεια στις ρυθμίσεις συστήματος."; "dialog_permission_location_description_ios" = "Παραχώρησε πρόσβαση στις Ρυθμίσεις -> Τοποθεσία."; @@ -334,11 +340,18 @@ "screen_advanced_settings_element_call_base_url" = "Προσαρμοσμένο URL βάσης κλήσεων Element"; "screen_advanced_settings_element_call_base_url_description" = "Όρισε μια προσαρμοσμένη διεύθυνση βάσης URL για κλήση Element."; "screen_advanced_settings_element_call_base_url_validation_error" = "Μη έγκυρη διεύθυνση URL, βεβαιώσου ότι έχεις συμπεριλάβει το πρωτόκολλο (http/https) και τη σωστή διεύθυνση."; -"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"; -"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; -"screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_create_room_access_section_anyone_option_description" = "Οποιοσδήποτε μπορεί να συμμετάσχει σε αυτό το δωμάτιο"; +"screen_create_room_access_section_anyone_option_title" = "Οποιοσδήποτε"; +"screen_create_room_access_section_header" = "Πρόσβαση Δωματίου"; +"screen_create_room_access_section_knocking_option_description" = "Οποιοσδήποτε μπορεί να ζητήσει να συμμετάσχει στο δωμάτιο, αλλά ένας διαχειριστής ή συντονιστής θα πρέπει να αποδεχθεί το αίτημα"; +"screen_create_room_access_section_knocking_option_title" = "Αίτημα συμμετοχής"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Μήνυμα (προαιρετικό)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Πάτα σε ένα μήνυμα και επέλεξε «%1$@» για να συμπεριληφθεί εδώ."; "screen_pinned_timeline_empty_state_headline" = "Καρφίτσωσε σημαντικά μηνύματα, ώστε να μπορούν να εντοπιστούν εύκολα"; "screen_reset_encryption_password_error" = "Συνέβη ένα άγνωστο σφάλμα. Έλεγξε ότι ο κωδικός πρόσβασης του λογαριασμού σου είναι σωστός και δοκίμασε ξανά."; @@ -593,7 +606,7 @@ "screen_qr_code_login_initial_state_item_3" = "Επιλογή %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "«Σύνδεση νέας συσκευής»"; "screen_qr_code_login_initial_state_item_4" = "Σάρωσε τον κωδικό QR με αυτήν τη συσκευή"; -"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; +"screen_qr_code_login_initial_state_subtitle" = "Διατίθεται μόνο εάν ο πάροχος του λογαριασμού σου το υποστηρίζει."; "screen_qr_code_login_initial_state_title" = "Άνοιγμα %1$@ σε άλλη συσκευή για να λήψη κωδικού QR"; "screen_qr_code_login_invalid_scan_state_description" = "Χρησιμοποίησε τον κωδικό QR που εμφανίζεται στην άλλη συσκευή."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Λάθος κωδικός QR"; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Άρση αποκλεισμού"; "screen_room_member_details_unblock_alert_description" = "Θα μπορείς να δεις ξανά όλα τα μηνύματα του."; "screen_room_member_details_unblock_user" = "Κατάργηση αποκλεισμού χρήστη"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Αποκλεισμός"; "screen_room_member_list_ban_member_confirmation_description" = "Δεν θα μπορεί να συμμετέχει ξανά σε αυτό το δωμάτιο εάν προσκληθεί."; "screen_room_member_list_ban_member_confirmation_title" = "Θες σίγουρα να αποκλείσεις αυτό το μέλος;"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Σύγκριση αριθμών"; "screen_session_verification_complete_subtitle" = "Η νέα σου συνεδρία έχει πλέον επαληθευτεί. Έχει πρόσβαση στα κρυπτογραφημένα μηνύματά σας και άλλοι χρήστες θα το βλέπουν ως αξιόπιστο."; "screen_session_verification_enter_recovery_key" = "Εισαγωγή κλειδιού ανάκτησης"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Απέδειξε ότι είσαι εσύ για να αποκτήσεις πρόσβαση στο κρυπτογραφημένο ιστορικό μηνυμάτων σου."; "screen_session_verification_open_existing_session_title" = "Άνοιξε μια υπάρχουσα συνεδρία"; "screen_session_verification_positive_button_canceled" = "Επανάληψη επαλήθευσης"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Αναμονή για αντιστοίχιση"; "screen_session_verification_ready_subtitle" = "Σύγκρινε ένα μοναδικό σύνολο emojis."; "screen_session_verification_request_accepted_subtitle" = "Σύγκρινε τα μοναδικά emoji και σιγουρέψου ότι εμφανίζονται με την ίδια σειρά."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Δεν ταιριάζουν"; "screen_session_verification_they_match" = "Ταιριάζουν"; "screen_session_verification_waiting_to_accept_subtitle" = "Αποδέξου το αίτημα για να ξεκινήσεις τη διαδικασία επαλήθευσης στην άλλη συνεδρία σου για να συνεχίσεις."; diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 3724e95cab..9b9b71021b 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Forgot password?"; "action_forward" = "Forward"; "action_go_back" = "Go back"; +"action_ignore" = "Ignore"; "action_invite" = "Invite"; "action_invite_friends" = "Invite people"; "action_invite_friends_to_app" = "Invite people to %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Dark"; "common_decryption_error" = "Decryption error"; "common_developer_options" = "Developer options"; +"common_device_id" = "Device ID"; "common_direct_chat" = "Direct chat"; "common_edited_suffix" = "(edited)"; "common_editing" = "Editing"; @@ -226,6 +228,7 @@ "common_username" = "Username"; "common_verification_cancelled" = "Verification cancelled"; "common_verification_complete" = "Verification complete"; +"common_verification_failed" = "Verification failed"; "common_verified" = "Verified"; "common_verify_device" = "Verify device"; "common_video" = "Video"; @@ -343,6 +346,9 @@ "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; "screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; "screen_join_room_knock_message_description" = "Message (optional)"; "screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; "screen_join_room_knock_sent_title" = "Request to join sent"; @@ -710,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Unblock"; "screen_room_member_details_unblock_alert_description" = "You'll be able to see all messages from them again."; "screen_room_member_details_unblock_user" = "Unblock user"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Ban"; "screen_room_member_list_ban_member_confirmation_description" = "They won’t be able to join this room again if invited."; "screen_room_member_list_ban_member_confirmation_title" = "Are you sure you want to ban this member?"; @@ -807,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Compare numbers"; "screen_session_verification_complete_subtitle" = "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted."; "screen_session_verification_enter_recovery_key" = "Enter recovery key"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Prove it’s you in order to access your encrypted message history."; "screen_session_verification_open_existing_session_title" = "Open an existing session"; "screen_session_verification_positive_button_canceled" = "Retry verification"; @@ -814,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Waiting to match"; "screen_session_verification_ready_subtitle" = "Compare a unique set of emojis."; "screen_session_verification_request_accepted_subtitle" = "Compare the unique emoji, ensuring they appear in the same order."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "They don’t match"; "screen_session_verification_they_match" = "They match"; "screen_session_verification_waiting_to_accept_subtitle" = "Accept the request to start the verification process in your other session to continue."; diff --git a/ElementX/Resources/Localizations/es.lproj/Localizable.strings b/ElementX/Resources/Localizations/es.lproj/Localizable.strings index 342eefefa1..0fd74cb2f6 100644 --- a/ElementX/Resources/Localizations/es.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/es.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "¿Olvidaste tu contraseña?"; "action_forward" = "Reenviar"; "action_go_back" = "Volver atrás"; +"action_ignore" = "Ignore"; "action_invite" = "Invitar"; "action_invite_friends" = "Invitar personas"; "action_invite_friends_to_app" = "Invita a alguien a %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Oscuro"; "common_decryption_error" = "Error de descifrado"; "common_developer_options" = "Opciones de desarrollador"; +"common_device_id" = "Device ID"; "common_direct_chat" = "Chat directo"; "common_edited_suffix" = "(editado)"; "common_editing" = "Edición"; @@ -226,6 +228,8 @@ "common_username" = "Usuario"; "common_verification_cancelled" = "Verificación cancelada"; "common_verification_complete" = "Verificación completada"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "Verificar dispositivo"; "common_video" = "Vídeo"; "common_voice_message" = "Mensaje de voz"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Confirma tu clave de recuperación"; "crash_detection_dialog_content" = "%1$@ se cerró inesperadamente la última vez que se lo usaste. ¿Quieres compartir un informe de error con nosotros?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Para permitir que la aplicación utilice la cámara, por favor concede el permiso en los ajustes del sistema."; "dialog_permission_generic" = "Por favor concede el permiso en los ajustes del sistema."; "dialog_permission_location_description_ios" = "Concede el permiso en Configuración -> Ubicación."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -502,7 +515,7 @@ "screen_invites_empty_list" = "Sin invitaciones"; "screen_invites_invited_you" = "%1$@ (%2$@) te invitó"; "screen_join_room_join_action" = "Join room"; -"screen_join_room_knock_action" = "Knock to join"; +"screen_join_room_knock_action" = "Send request to join"; "screen_join_room_space_not_supported_description" = "%1$@ does not support spaces yet. You can access spaces on web."; "screen_join_room_space_not_supported_title" = "Spaces are not supported yet"; "screen_join_room_subtitle_knock" = "Click the button below and a room administrator will be notified. You’ll be able to join the conversation once approved."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Desbloquear"; "screen_room_member_details_unblock_alert_description" = "Podrás ver todos sus mensajes de nuevo."; "screen_room_member_details_unblock_user" = "Desbloquear usuario"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Prohibir"; "screen_room_member_list_ban_member_confirmation_description" = "No podrán volver a unirse a esta sala si son invitados."; "screen_room_member_list_ban_member_confirmation_title" = "¿Estás seguro de que quieres prohibir a este miembro?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Comparar números"; "screen_session_verification_complete_subtitle" = "Tu nueva sesión ya está verificada. Tienes acceso a tus mensajes cifrados y otros usuarios lo considerarán de confianza."; "screen_session_verification_enter_recovery_key" = "Introduzca la clave de recuperación"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Demuestra que eres tú para acceder a tu historial de mensajes cifrados."; "screen_session_verification_open_existing_session_title" = "Abrir una sesión existente"; "screen_session_verification_positive_button_canceled" = "Reintentar la verificación"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Esperando a que coincida"; "screen_session_verification_ready_subtitle" = "Compara un conjunto único de emojis."; "screen_session_verification_request_accepted_subtitle" = "Compara los emoji, asegurándote de que aparecen en el mismo orden."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "No coinciden"; "screen_session_verification_they_match" = "Coinciden"; "screen_session_verification_waiting_to_accept_subtitle" = "Acepta la solicitud para iniciar el proceso de verificación en tu otra sesión para continuar."; diff --git a/ElementX/Resources/Localizations/et.lproj/Localizable.strings b/ElementX/Resources/Localizations/et.lproj/Localizable.strings index fdd05262ed..49c57f1249 100644 --- a/ElementX/Resources/Localizations/et.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/et.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Kas unustasid salasõna?"; "action_forward" = "Edasta"; "action_go_back" = "Tagasi eelmisesse vaatesse"; +"action_ignore" = "Eira"; "action_invite" = "Kutsu"; "action_invite_friends" = "Kutsu osalejaid"; "action_invite_friends_to_app" = "Kutsu huvilisi kasutama rakendust %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Tume"; "common_decryption_error" = "Dekrüptimisviga"; "common_developer_options" = "Arendaja valikud"; +"common_device_id" = "Seadme tunnus"; "common_direct_chat" = "Otsevestlus"; "common_edited_suffix" = "(muudetud)"; "common_editing" = "Muutmine"; @@ -226,6 +228,8 @@ "common_username" = "Kasutajanimi"; "common_verification_cancelled" = "Verifitseerimine on katkestatud"; "common_verification_complete" = "Verifitseerimine on tehtud"; +"common_verification_failed" = "Verifitseerimine ei õnnestunud"; +"common_verified" = "Verifitseeritud"; "common_verify_device" = "Verifitseeri seade"; "common_video" = "Video"; "common_voice_message" = "Häälsõnum"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Sisesta oma taastevõti"; "crash_detection_dialog_content" = "%1$@ jooksis kokku viimati, kui seda kasutasid. Kas tahaksid selle kohta meile veateate saata?"; "crypto_identity_change_pin_violation" = "Kasutaja %1$@ võrguidentiteet tundub olema muutunud. %2$@"; +"crypto_identity_change_pin_violation_new" = "Kasutaja %1$@ %2$@ võrguidentiteet tundub olema muutunud. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Selleks, et rakendus saaks kaamerat kasutada, palun luba see süsteemi seadistuses."; "dialog_permission_generic" = "Palun luba süsteemi seadistustest vajalikud õigused."; "dialog_permission_location_description_ios" = "Luba õigused Seadistused -> Asukoht valikust."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Ligipääs jututoale"; "screen_create_room_access_section_knocking_option_description" = "Kõik võivad paluda selle jututoaga liitumist, kuid peakasutaja või moderaator peavad selle kinnitama"; "screen_create_room_access_section_knocking_option_title" = "Küsi võimalust liitumiseks"; +"screen_join_room_cancel_knock_action" = "Tühista liitumispalve"; +"screen_join_room_cancel_knock_alert_confirmation" = "Jah, tühista"; +"screen_join_room_cancel_knock_alert_description" = "Kas sa oled kindel, et soovid tühistada oma palve jututoaga liitumiseks?"; +"screen_join_room_cancel_knock_alert_title" = "Tühista liitumispalve"; +"screen_join_room_knock_message_description" = "Selgitus (kui soovid lisada)"; +"screen_join_room_knock_sent_description" = "Kui sinu liitumispalvega ollakse nõus, siis saad kutse jututoaga liitumiseks."; +"screen_join_room_knock_sent_title" = "Liitumispalve on saadetud"; "screen_pinned_timeline_empty_state_description" = "Siia lisamiseks vajuta sõnumil ja vali „%1$@“."; "screen_pinned_timeline_empty_state_headline" = "Et olulisi sõnumeid oleks lihtsam leida, tõsta nad esile"; "screen_reset_encryption_password_error" = "Tekkis teadmata viga. Palun kontrolli, kas sinu kasutajakonto salasõna on õige ja proovi uuesti."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Eemalda blokeering"; "screen_room_member_details_unblock_alert_description" = "Nüüd näed sa jälle kõiki tema sõnumeid"; "screen_room_member_details_unblock_user" = "Eemalda kasutajalt blokeering"; +"screen_room_member_details_verify_button_subtitle" = "Kasutaja verifitseerimiseks kasuta veebirakendust."; +"screen_room_member_details_verify_button_title" = "Verifitseeri kasutaja %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Sea suhtluskeeld"; "screen_room_member_list_ban_member_confirmation_description" = "Ta ei saa selle jututoaga liituda isegi kutse olemasolul."; "screen_room_member_list_ban_member_confirmation_title" = "Kas sa oled kindel, et soovid sellele kasutajale seada suhtluskeelu?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Võrdle numbreid"; "screen_session_verification_complete_subtitle" = "Sinu uus sessioon on nüüd verifitseeritud. Sellel sessioonil on nüüd ligipääs sinu krüptitud sõnumitele ja teised osapooled näevad teda usaldusväärsena."; "screen_session_verification_enter_recovery_key" = "Sisesta taastevõti"; +"screen_session_verification_failed_subtitle" = "Kas verifitseerimine aegus, teine osapool keeldus vastanast või tekkis vastuste mittevastavus."; "screen_session_verification_open_existing_session_subtitle" = "Saamaks ligipääsu krüptitud sõnumite ajaloole tõesta et tegemist on sinuga."; "screen_session_verification_open_existing_session_title" = "Ava olemasolev sessioon"; "screen_session_verification_positive_button_canceled" = "Proovi verifitseerimist uuesti"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Ootame kinnitust sobivusele"; "screen_session_verification_ready_subtitle" = "Võrdle unikaalset emojide kombinatsiooni"; "screen_session_verification_request_accepted_subtitle" = "Võrdle unikaalset emojide kombinatsiooni ning kontrolli, et nad on täpselt samas järjekorras."; +"screen_session_verification_request_details_timestamp" = "Sisselogitud"; +"screen_session_verification_request_footer" = "Jätka vaid siis, kui sina algatasid verifitseerimise."; +"screen_session_verification_request_subtitle" = "Hoidmaks oma sõnumiajalugu turvatuna verifitseeri teine seade."; +"screen_session_verification_request_title" = "Verifitseerimispäring"; "screen_session_verification_they_dont_match" = "Nad ei klapi omavahel"; "screen_session_verification_they_match" = "Nad klapivad omavahel"; "screen_session_verification_waiting_to_accept_subtitle" = "Jätkamaks nõustu verifitseerimisprotsessi alustamisega oma teises sessioonis."; diff --git a/ElementX/Resources/Localizations/fa.lproj/Localizable.strings b/ElementX/Resources/Localizations/fa.lproj/Localizable.strings index 60d656c6db..b995c0ae8c 100644 --- a/ElementX/Resources/Localizations/fa.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/fa.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "گذرواژه را فراموش کردید؟"; "action_forward" = "پیشروی"; "action_go_back" = "پس‌روی"; +"action_ignore" = "Ignore"; "action_invite" = "دعوت"; "action_invite_friends" = "دعوت افراد"; "action_invite_friends_to_app" = "دعوت به %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "تیره"; "common_decryption_error" = "خطای رمزگشایی"; "common_developer_options" = "گزینه‌های توسعه دهنده"; +"common_device_id" = "Device ID"; "common_direct_chat" = "گپ مستقیم"; "common_edited_suffix" = "(ویراسته)"; "common_editing" = "ویرایش"; @@ -226,6 +228,8 @@ "common_username" = "نام کاربری"; "common_verification_cancelled" = "تأیید لغو شد"; "common_verification_complete" = "تأیید کامل شد"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "تأیید افزاره"; "common_video" = "ویدیو"; "common_voice_message" = "پیام صوتی"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "ورود کلید بازیابیتان"; "crash_detection_dialog_content" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; "crypto_identity_change_pin_violation" = "به نظر می‌رسد هویت %1$@ تغییر کرده. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "In order to let the application use the camera, please grant the permission in the system settings."; "dialog_permission_generic" = "لطفاً در تنظیمات سامانه اجازه بدهید."; "dialog_permission_location_description_ios" = "اعطای دسترسی در تنظیمات -> مکان."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "رفع انسداد"; "screen_room_member_details_unblock_alert_description" = "قادر خواهید بود دوباره همهٔ پیام‌هایش را ببینید."; "screen_room_member_details_unblock_user" = "رفع انسداد کاربر"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "تحریم"; "screen_room_member_list_ban_member_confirmation_description" = "They won’t be able to join this room again if invited."; "screen_room_member_list_ban_member_confirmation_title" = "Are you sure you want to ban this member?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "مقایسهٔ اعداد"; "screen_session_verification_complete_subtitle" = "نشست جدید شما اکنون تأیید شده‌است. این نشست به پیام‌های رمزگذاری شده‌ی شما دسترسی داشته و سایر کاربران نیز این نشست را قابل اعتماد می دانند."; "screen_session_verification_enter_recovery_key" = "ورود کلید بازیابی"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Prove it’s you in order to access your encrypted message history."; "screen_session_verification_open_existing_session_title" = "گشودن نشستی موجود"; "screen_session_verification_positive_button_canceled" = "تلاش برای تأیید دوباره"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "منتظر تطابق"; "screen_session_verification_ready_subtitle" = "مقایسهٔ مجموعه‌ای یکتا از شکلک‌ها."; "screen_session_verification_request_accepted_subtitle" = "شکلک‌ها را مقایسه کنید، از ترتیب نمایش آنان نیز مطمئن شوید."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "مطابق نیستند"; "screen_session_verification_they_match" = "مطابقند"; "screen_session_verification_waiting_to_accept_subtitle" = "Accept the request to start the verification process in your other session to continue."; diff --git a/ElementX/Resources/Localizations/fr.lproj/Localizable.strings b/ElementX/Resources/Localizations/fr.lproj/Localizable.strings index 641e4778ad..f2b1734bc5 100644 --- a/ElementX/Resources/Localizations/fr.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/fr.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Mot de passe oublié ?"; "action_forward" = "Transférer"; "action_go_back" = "Retour"; +"action_ignore" = "Ignorer"; "action_invite" = "Inviter"; "action_invite_friends" = "Inviter des amis"; "action_invite_friends_to_app" = "Inviter des amis à %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Sombre"; "common_decryption_error" = "Erreur de déchiffrement"; "common_developer_options" = "Options pour les développeurs"; +"common_device_id" = "Identifiant de session"; "common_direct_chat" = "Discussion à deux"; "common_edited_suffix" = "(modifié)"; "common_editing" = "Édition"; @@ -226,6 +228,8 @@ "common_username" = "Nom d’utilisateur"; "common_verification_cancelled" = "Vérification annulée"; "common_verification_complete" = "Vérification terminée"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Vérifié(e)"; "common_verify_device" = "Vérifier la session"; "common_video" = "Vidéo"; "common_voice_message" = "Message vocal"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Confirmer votre clé de récupération"; "crash_detection_dialog_content" = "%1$@ s’est arrêté la dernière fois qu’il a été utilisé. Souhaitez-vous partager un rapport d’incident avec nous ?"; "crypto_identity_change_pin_violation" = "L'identité de %1$@ semble avoir changé. %2$@"; +"crypto_identity_change_pin_violation_new" = "L'identité de %1$@ %2$@ semble avoir changé. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Pour permettre à l’application d’utiliser l’appareil photo, veuillez accorder l’autorisation dans les paramètres du système."; "dialog_permission_generic" = "Veuillez accorder l’autorisation dans les paramètres du système."; "dialog_permission_location_description_ios" = "Autorisez l’accès dans Paramètres / Localisation."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Accès au salon"; "screen_create_room_access_section_knocking_option_description" = "Tout le monde peut demander à rejoindre le salon, mais un administrateur ou un modérateur devra accepter la demande"; "screen_create_room_access_section_knocking_option_title" = "Demander à rejoindre"; +"screen_join_room_cancel_knock_action" = "Annuler la demande"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (facultatif)"; +"screen_join_room_knock_sent_description" = "Vous recevrez une invitation à rejoindre le salon si votre demande est acceptée."; +"screen_join_room_knock_sent_title" = "Demande de rejoindre le salon envoyée"; "screen_pinned_timeline_empty_state_description" = "Cliquez (clic long) sur un message et choisissez « %1$@ » pour qu‘il apparaisse ici."; "screen_pinned_timeline_empty_state_headline" = "Épinglez les messages importants pour leur donner plus de visibilité"; "screen_reset_encryption_password_error" = "Une erreur s'est produite. Vérifiez que le mot de passe de votre compte est correct et réessayez."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Débloquer"; "screen_room_member_details_unblock_alert_description" = "Vous pourrez à nouveau voir tous ses messages."; "screen_room_member_details_unblock_user" = "Débloquer l’utilisateur"; +"screen_room_member_details_verify_button_subtitle" = "Utilisez l'application Web pour vérifier cet utilisateur."; +"screen_room_member_details_verify_button_title" = "Vérifier %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Bannir"; "screen_room_member_list_ban_member_confirmation_description" = "L‘utilisateur ne pourra pas rejoindre le salon à nouveau, même si il est invité."; "screen_room_member_list_ban_member_confirmation_title" = "Êtes-vous certain de vouloir bannir ce membre?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Comparez les nombres"; "screen_session_verification_complete_subtitle" = "Votre nouvelle session est désormais vérifiée. Elle a accès à vos messages chiffrés et les autres utilisateurs la verront identifiée comme fiable."; "screen_session_verification_enter_recovery_key" = "Utiliser la clé de récupération"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Prouvez qu’il s’agit bien de vous pour accéder à l’historique de vos messages chiffrés."; "screen_session_verification_open_existing_session_title" = "Ouvrir une session existante"; "screen_session_verification_positive_button_canceled" = "Réessayer la vérification"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "En attente de correspondance"; "screen_session_verification_ready_subtitle" = "Comparer un groupe unique d’Emojis."; "screen_session_verification_request_accepted_subtitle" = "Comparez les emoji uniques en veillant à ce qu’ils apparaissent dans le même ordre."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Continuez uniquement si c'est vous qui avez commencé cette vérification."; +"screen_session_verification_request_subtitle" = "Vérifiez l'autre appareil pour sécuriser l'historique de vos messages."; +"screen_session_verification_request_title" = "Vérification demandée"; "screen_session_verification_they_dont_match" = "Ils ne correspondent pas"; "screen_session_verification_they_match" = "Ils correspondent"; "screen_session_verification_waiting_to_accept_subtitle" = "Pour continuer, acceptez la demande de lancement de la procédure de vérification dans votre autre session."; diff --git a/ElementX/Resources/Localizations/hu.lproj/Localizable.strings b/ElementX/Resources/Localizations/hu.lproj/Localizable.strings index d4fc18347b..cf0a7dd602 100644 --- a/ElementX/Resources/Localizations/hu.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/hu.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Elfelejtette a jelszót?"; "action_forward" = "Tovább"; "action_go_back" = "Visszalépés"; +"action_ignore" = "Mellőzés"; "action_invite" = "Meghívás"; "action_invite_friends" = "Ismerősök meghívása"; "action_invite_friends_to_app" = "Ismerősök meghívása ide: %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Sötét"; "common_decryption_error" = "Visszafejtési hiba"; "common_developer_options" = "Fejlesztői beállítások"; +"common_device_id" = "Eszközazonosító"; "common_direct_chat" = "Közvetlen csevegés"; "common_edited_suffix" = "(szerkesztve)"; "common_editing" = "Szerkesztés"; @@ -226,6 +228,8 @@ "common_username" = "Felhasználónév"; "common_verification_cancelled" = "Az ellenőrzés megszakítva"; "common_verification_complete" = "Az ellenőrzés befejeződött"; +"common_verification_failed" = "Az ellenőrzés sikertelen"; +"common_verified" = "Ellenőrizve"; "common_verify_device" = "Eszköz ellenőrzése"; "common_video" = "Videó"; "common_voice_message" = "Hangüzenet"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Helyreállítási kulcs megerősítése"; "crash_detection_dialog_content" = "Az %1$@ összeomlott a legutóbbi használata óta. Megosztja velünk az összeomlás-jelentést?"; "crypto_identity_change_pin_violation" = "Úgy tűnik, hogy %1$@ személyazonossága megváltozott. %2$@"; +"crypto_identity_change_pin_violation_new" = "Úgy tűnik, hogy %1$@ %2$@ személyazonossága megváltozott. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Hogy az alkalmazás használhassa a kamerát, adja meg az engedélyt a rendszerbeállításokban."; "dialog_permission_generic" = "Adja meg az engedélyt a rendszerbeállításokban."; "dialog_permission_location_description_ios" = "Adjon hozzáférést a Beállítások -> Hely menüpontban."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Szobahozzáférés"; "screen_create_room_access_section_knocking_option_description" = "Bárki kérheti, hogy csatlakozzon a szobához, de egy adminisztrátornak vagy moderátornak el kell fogadnia a kérést"; "screen_create_room_access_section_knocking_option_title" = "Csatlakozás kérése"; +"screen_join_room_cancel_knock_action" = "Kérés visszavonása"; +"screen_join_room_cancel_knock_alert_confirmation" = "Igen, visszavonás"; +"screen_join_room_cancel_knock_alert_description" = "Biztos, hogy visszavonja a szobához való csatlakozási kérését?"; +"screen_join_room_cancel_knock_alert_title" = "Csatlakozási kérés visszavonása"; +"screen_join_room_knock_message_description" = "Üzenet (nem kötelező)"; +"screen_join_room_knock_sent_description" = "Ha a kérését elfogadják, meghívót kap a szobához való csatlakozáshoz."; +"screen_join_room_knock_sent_title" = "Csatlakozási kérés elküldve"; "screen_pinned_timeline_empty_state_description" = "Nyomjon hosszan az üzenetre, és válassza a „%1$@” lehetőséget, hogy itt szerepeljen."; "screen_pinned_timeline_empty_state_headline" = "Tűzze ki a fontos üzeneteket, hogy könnyen felfedezhetők legyenek"; "screen_reset_encryption_password_error" = "Ismeretlen hiba történt. Ellenőrizze, hogy a fiókja jelszava helyes-e, és próbálja meg újra."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Letiltás feloldása"; "screen_room_member_details_unblock_alert_description" = "Újra láthatja az összes üzenetét."; "screen_room_member_details_unblock_user" = "Felhasználó kitiltásának feloldása"; +"screen_room_member_details_verify_button_subtitle" = "Használja a webes alkalmazást a felhasználó ellenőrzéséhez."; +"screen_room_member_details_verify_button_title" = "A(z) %1$@ ellenőrzése"; "screen_room_member_list_ban_member_confirmation_action" = "Kitiltás"; "screen_room_member_list_ban_member_confirmation_description" = "Többé nem csatlakozhat ehhez a szobához, akkor sem, ha meghívják."; "screen_room_member_list_ban_member_confirmation_title" = "Biztos, hogy kitiltja ezt a tagot?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Számok összehasonlítása"; "screen_session_verification_complete_subtitle" = "Az új munkamenete most már ellenőrizve van. Eléri a titkosított üzeneteit, és a többi felhasználó is megbízhatónak fogja látni."; "screen_session_verification_enter_recovery_key" = "Adja meg a helyreállítási kulcsot"; +"screen_session_verification_failed_subtitle" = "A kérés túllépte az időkorlátot, el lett utasítva, vagy ellenőrzési eltérés történt."; "screen_session_verification_open_existing_session_subtitle" = "Bizonyítsa, hogy valóban Ön az, hogy elérje a titkosított üzeneteinek előzményeit."; "screen_session_verification_open_existing_session_title" = "Meglévő munkamenet megnyitása"; "screen_session_verification_positive_button_canceled" = "Ellenőrzés újrapróbálása"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Várakozás az egyezésre"; "screen_session_verification_ready_subtitle" = "Egyedi emodzsik összehasonlítása."; "screen_session_verification_request_accepted_subtitle" = "Hasonlítsa össze az egyedi emodzsikat, meggyőződve arról, hogy azonos a sorrendjük."; +"screen_session_verification_request_details_timestamp" = "Bejelentkezve"; +"screen_session_verification_request_footer" = "Csak akkor folytassa, ha Ön kezdeményezte ezt az ellenőrzést."; +"screen_session_verification_request_subtitle" = "Az üzenetelőzmények biztonságának megőrzése érdekében ellenőrizze a másik eszközt."; +"screen_session_verification_request_title" = "Ellenőrzés kérve"; "screen_session_verification_they_dont_match" = "Nem egyeznek"; "screen_session_verification_they_match" = "Megegyeznek"; "screen_session_verification_waiting_to_accept_subtitle" = "A folytatáshoz fogadja el az ellenőrzési folyamat indítási kérését a másik munkamenetében."; diff --git a/ElementX/Resources/Localizations/id.lproj/Localizable.strings b/ElementX/Resources/Localizations/id.lproj/Localizable.strings index b29e7e82c9..df2a4c7cb7 100644 --- a/ElementX/Resources/Localizations/id.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/id.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Lupa kata sandi?"; "action_forward" = "Teruskan"; "action_go_back" = "Kembali"; +"action_ignore" = "Ignore"; "action_invite" = "Undang"; "action_invite_friends" = "Undang orang-orang"; "action_invite_friends_to_app" = "Undang orang-orang ke %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Gelap"; "common_decryption_error" = "Kesalahan dekripsi"; "common_developer_options" = "Opsi pengembang"; +"common_device_id" = "Device ID"; "common_direct_chat" = "Obrolan langsung"; "common_edited_suffix" = "(disunting)"; "common_editing" = "Penyuntingan"; @@ -226,6 +228,8 @@ "common_username" = "Nama pengguna"; "common_verification_cancelled" = "Verifikasi dibatalkan"; "common_verification_complete" = "Verifikasi selesai"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "Verifikasi perangkat"; "common_video" = "Video"; "common_voice_message" = "Pesan suara"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Konfirmasi kunci pemulihan Anda"; "crash_detection_dialog_content" = "%1$@ mengalami kemogokan saat terakhir kali digunakan. Apakah Anda ingin berbagi laporan kerusakan dengan kami?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Supaya aplikasinya dapat menggunakan kamera, berikan izin dalam pengaturan sistem."; "dialog_permission_generic" = "Silakan memberikan izin dalam pengaturan sistem."; "dialog_permission_location_description_ios" = "Berikan akses dalam Pengaturan -> Lokasi."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Buka blokir"; "screen_room_member_details_unblock_alert_description" = "Anda akan dapat melihat semua pesan dari mereka lagi."; "screen_room_member_details_unblock_user" = "Buka blokir pengguna"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Cekal"; "screen_room_member_list_ban_member_confirmation_description" = "Mereka tidak akan dapat bergabung ke ruangan ini lagi jika diundang."; "screen_room_member_list_ban_member_confirmation_title" = "Apakah Anda yakin ingin mencekal anggota ini?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Bandingkan angka"; "screen_session_verification_complete_subtitle" = "Sesi baru Anda sekarang diverifikasi. Ini memiliki akses ke pesan terenkripsi Anda, dan pengguna lain akan melihatnya sebagai tepercaya."; "screen_session_verification_enter_recovery_key" = "Masukkan kunci pemulihan"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Buktikan bahwa ini memang Anda untuk mengakses riwayat pesan terenkripsi Anda."; "screen_session_verification_open_existing_session_title" = "Buka sesi yang sudah ada"; "screen_session_verification_positive_button_canceled" = "Verifikasi ulang"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Menunggu untuk mencocokkan"; "screen_session_verification_ready_subtitle" = "Bandingkan satu set emoji yang unik."; "screen_session_verification_request_accepted_subtitle" = "Bandingkan emoji unik, dan pastikan emoji tersebut muncul dalam urutan yang sama."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Mereka tidak cocok"; "screen_session_verification_they_match" = "Mereka cocok"; "screen_session_verification_waiting_to_accept_subtitle" = "Terima permintaan untuk memulai proses verifikasi di sesi Anda yang lain untuk melanjutkan."; diff --git a/ElementX/Resources/Localizations/it.lproj/Localizable.strings b/ElementX/Resources/Localizations/it.lproj/Localizable.strings index 6822eafb86..d9a92ad9c6 100644 --- a/ElementX/Resources/Localizations/it.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/it.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Password dimenticata?"; "action_forward" = "Inoltra"; "action_go_back" = "Indietro"; +"action_ignore" = "Ignore"; "action_invite" = "Invita"; "action_invite_friends" = "Invita persone"; "action_invite_friends_to_app" = "Invita persone su %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Scuro"; "common_decryption_error" = "Errore di decrittazione"; "common_developer_options" = "Opzioni sviluppatore"; +"common_device_id" = "Device ID"; "common_direct_chat" = "Conversazione diretta"; "common_edited_suffix" = "(modificato)"; "common_editing" = "Modifica in corso"; @@ -226,6 +228,8 @@ "common_username" = "Nome utente"; "common_verification_cancelled" = "Verifica annullata"; "common_verification_complete" = "Verifica completata"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "Verifica dispositivo"; "common_video" = "Video"; "common_voice_message" = "Messaggio vocale"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Inserisci la chiave di recupero"; "crash_detection_dialog_content" = "%1$@ si è chiuso inaspettatamente l'ultima volta che è stato usato. Vuoi condividere con noi un rapporto sull'arresto anomalo?"; "crypto_identity_change_pin_violation" = "L'identità di %1$@ sembra essere cambiata. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Per permettere all'applicazione di usare la fotocamera, concedi l'autorizzazione nelle impostazioni di sistema."; "dialog_permission_generic" = "Concedi l'autorizzazione nelle impostazioni di sistema."; "dialog_permission_location_description_ios" = "Concedi l'accesso in Impostazioni -> Condividi la mia posizione."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Premi su un messaggio e scegli “%1$@” per includerlo qui."; "screen_pinned_timeline_empty_state_headline" = "Fissa i messaggi importanti così che possano essere trovati facilmente"; "screen_reset_encryption_password_error" = "Si è verificato un errore sconosciuto. Controlla che la password del tuo account sia corretta e riprova."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Sblocca"; "screen_room_member_details_unblock_alert_description" = "Potrai vedere di nuovo tutti i suoi messaggi."; "screen_room_member_details_unblock_user" = "Sblocca utente"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Escludi"; "screen_room_member_list_ban_member_confirmation_description" = "Non potrà entrare nuovamente in questa stanza se invitato."; "screen_room_member_list_ban_member_confirmation_title" = "Vuoi davvero escludere questo membro?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Confronta i numeri"; "screen_session_verification_complete_subtitle" = "La tua nuova sessione è ora verificata. Ha accesso ai tuoi messaggi crittografati e gli altri utenti la vedranno come attendibile."; "screen_session_verification_enter_recovery_key" = "Inserisci la chiave di recupero"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Dimostra la tua identità per accedere alla cronologia dei messaggi crittografati."; "screen_session_verification_open_existing_session_title" = "Apri una sessione esistente"; "screen_session_verification_positive_button_canceled" = "Riprova la verifica"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "In attesa di un riscontro"; "screen_session_verification_ready_subtitle" = "Confronta un set unico di emoji."; "screen_session_verification_request_accepted_subtitle" = "Confronta le emoji uniche, assicurandoti che appaiano nello stesso ordine."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Non corrispondono"; "screen_session_verification_they_match" = "Corrispondono"; "screen_session_verification_waiting_to_accept_subtitle" = "Accetta la richiesta di avviare il processo di verifica nell'altra sessione per continuare."; diff --git a/ElementX/Resources/Localizations/ka.lproj/Localizable.strings b/ElementX/Resources/Localizations/ka.lproj/Localizable.strings index c5915b3f2c..ad341b3866 100644 --- a/ElementX/Resources/Localizations/ka.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ka.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "დაგავიწყდათ პაროლი?"; "action_forward" = "გადაგზავნა"; "action_go_back" = "Go back"; +"action_ignore" = "Ignore"; "action_invite" = "მოწვევა"; "action_invite_friends" = "ხალხის მოწვევა"; "action_invite_friends_to_app" = "ადამიანების დამატება %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "მუქი"; "common_decryption_error" = "გაშიფვრის შეცდომა"; "common_developer_options" = "დეველოპერის პარამეტრები"; +"common_device_id" = "Device ID"; "common_direct_chat" = "პირდაპირი ჩატი"; "common_edited_suffix" = "(რედაქტირებულია)"; "common_editing" = "რედაქტირება"; @@ -226,6 +228,8 @@ "common_username" = "მომხმარებლის სახელი"; "common_verification_cancelled" = "დადასტურება გაუქმდა"; "common_verification_complete" = "დადასტურება დასრულებულია"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "დაადასტურეთ მოწყობილობა"; "common_video" = "ვიდეო"; "common_voice_message" = "ხმოვანი შეტყობინება"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "შეიყვანეთ აღდგენის გასაღები"; "crash_detection_dialog_content" = "%1$@ ავარიულად გაითიშა ბოლოს გამოიყენებისას. გსურთ, გამოგვიგზავნოთ ავარიული გათიშვის ჟურნალი?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "იმისათვის, რომ აპლიკაციამ გამოიყენოს კამერა, გთხოვთ, მიანიჭოთ ნებართვა სისტემის პარამეტრებში."; "dialog_permission_generic" = "გთხოვთ, მიანიჭოთ ნებართვა სისტემის პარამეტრებში."; "dialog_permission_location_description_ios" = "მიანიჭეთ წვდომა პარამეტრებში -> ლოკაციაში."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -502,7 +515,7 @@ "screen_invites_empty_list" = "მოწვევები არ არის"; "screen_invites_invited_you" = "%1$@ (%2$@) მოგიწვიათ"; "screen_join_room_join_action" = "Join room"; -"screen_join_room_knock_action" = "Knock to join"; +"screen_join_room_knock_action" = "Send request to join"; "screen_join_room_space_not_supported_description" = "%1$@ does not support spaces yet. You can access spaces on web."; "screen_join_room_space_not_supported_title" = "Spaces are not supported yet"; "screen_join_room_subtitle_knock" = "Click the button below and a room administrator will be notified. You’ll be able to join the conversation once approved."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "განბლოკვა"; "screen_room_member_details_unblock_alert_description" = "თქვენ კვლავ შეძლებთ მათგან ყველა შეტყობინების ნახვას."; "screen_room_member_details_unblock_user" = "Მომხმარებლის განბლოკვა"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Ban"; "screen_room_member_list_ban_member_confirmation_description" = "They won’t be able to join this room again if invited."; "screen_room_member_list_ban_member_confirmation_title" = "Are you sure you want to ban this member?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "შეადარეთ რიცხვები"; "screen_session_verification_complete_subtitle" = "თქვენი ახალი სესია დადასტურებულია. მას აქვს წვდომა დაშიფრულ შეტყობინებებზე და სხვა მომხმარებლები მას სანდოდ ხედავენ."; "screen_session_verification_enter_recovery_key" = "Enter recovery key"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "დაამტკიცეთ, რომ ეს თქვენ ხართ, რათა მიიღოთ წვდომა თქვენი დაშიფრული შეტყობინებების ისტორიასთან."; "screen_session_verification_open_existing_session_title" = "არსებული სესიის გახსნა"; "screen_session_verification_positive_button_canceled" = "დადასტურების ხელახლა ცდა"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "ველოდებით დამთხვევას"; "screen_session_verification_ready_subtitle" = "შეადარეთ ემოციების უნიკალური ნაკრები."; "screen_session_verification_request_accepted_subtitle" = "შეადარეთ უნიკალური ემოჯი, დარწმუნდით, რომ ისინი ერთი დ იმავე თანმიმდევრობით გამოჩნდნენ."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "ისინი არ ემთხვევიან ერთმანეთს"; "screen_session_verification_they_match" = "ისინი ემთხვევიან ერთმანეთს"; "screen_session_verification_waiting_to_accept_subtitle" = "მიიღეთ დადასტურების მოთხოვნა თქვენს სხვა სესიაში ამ პროცესის გასაგრძელებლად."; diff --git a/ElementX/Resources/Localizations/nl.lproj/Localizable.strings b/ElementX/Resources/Localizations/nl.lproj/Localizable.strings index 50a55f8d49..a791abb7c3 100644 --- a/ElementX/Resources/Localizations/nl.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/nl.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Wachtwoord vergeten?"; "action_forward" = "Doorsturen"; "action_go_back" = "Terug"; +"action_ignore" = "Ignore"; "action_invite" = "Uitnodigen"; "action_invite_friends" = "Mensen uitnodigen"; "action_invite_friends_to_app" = "Nodig mensen uit voor %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Donker"; "common_decryption_error" = "Decryptie fout"; "common_developer_options" = "Ontwikkelaarsopties"; +"common_device_id" = "Device ID"; "common_direct_chat" = "Directe chat"; "common_edited_suffix" = "(bewerkt)"; "common_editing" = "Bewerken"; @@ -226,6 +228,8 @@ "common_username" = "Gebruikersnaam"; "common_verification_cancelled" = "Verificatie geannuleerd"; "common_verification_complete" = "Verificatie voltooid"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "Apparaat verifiëren"; "common_video" = "Video"; "common_voice_message" = "Spraakbericht"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Voer je herstelsleutel in"; "crash_detection_dialog_content" = "%1$@ crashte de laatste keer dat het werd gebruikt. Wil je een crashrapport met ons delen?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Geef toestemming in de systeeminstellingen om de applicatie de camera te laten gebruiken."; "dialog_permission_generic" = "Geef hiervoor toestemming in de systeeminstellingen."; "dialog_permission_location_description_ios" = "Verleen toegang via Instellingen -> Locatie."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Deblokkeren"; "screen_room_member_details_unblock_alert_description" = "Je zult alle berichten van hen weer kunnen zien."; "screen_room_member_details_unblock_user" = "Gebruiker deblokkeren"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Verbannen"; "screen_room_member_list_ban_member_confirmation_description" = "Ze kunnen niet meer toetreden tot deze kamer als ze worden uitgenodigd."; "screen_room_member_list_ban_member_confirmation_title" = "Weet je zeker dat je dit lid wilt verbannen?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Vergelijk getallen"; "screen_session_verification_complete_subtitle" = "Je nieuwe sessie is nu geverifieerd. Het heeft toegang tot je versleutelde berichten en andere gebruikers zullen het als vertrouwd beschouwen."; "screen_session_verification_enter_recovery_key" = "Voer herstelsleutel in"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Bewijs dat jij het bent om toegang te krijgen tot je versleutelde berichtgeschiedenis."; "screen_session_verification_open_existing_session_title" = "Open een bestaande sessie"; "screen_session_verification_positive_button_canceled" = "Verificatie opnieuw proberen"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Wachten om te vergelijken"; "screen_session_verification_ready_subtitle" = "Vergelijk een unieke combinatie van emoji's."; "screen_session_verification_request_accepted_subtitle" = "Vergelijk de unieke emoji's, ze dienen in dezelfde volgorde te worden weergegeven."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Ze komen niet overeen"; "screen_session_verification_they_match" = "Ze komen overeen"; "screen_session_verification_waiting_to_accept_subtitle" = "Accepteer het verzoek tot verificatie in je andere sessie om door te gaan."; diff --git a/ElementX/Resources/Localizations/pl.lproj/Localizable.strings b/ElementX/Resources/Localizations/pl.lproj/Localizable.strings index cc8aa07e2f..a8d5f00b19 100644 --- a/ElementX/Resources/Localizations/pl.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pl.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Nie pamiętasz hasła?"; "action_forward" = "Przekaż dalej"; "action_go_back" = "Wróć"; +"action_ignore" = "Ignore"; "action_invite" = "Zaproś"; "action_invite_friends" = "Zaproś znajomych"; "action_invite_friends_to_app" = "Zaproś znajomych do %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Ciemny"; "common_decryption_error" = "Błąd deszyfrowania"; "common_developer_options" = "Opcje programisty"; +"common_device_id" = "Device ID"; "common_direct_chat" = "Czat prywatny"; "common_edited_suffix" = "(edytowane)"; "common_editing" = "Edytowanie"; @@ -226,6 +228,8 @@ "common_username" = "Nazwa użytkownika"; "common_verification_cancelled" = "Weryfikacja anulowana"; "common_verification_complete" = "Weryfikacja zakończona"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "Weryfikuj urządzenie"; "common_video" = "Film"; "common_voice_message" = "Wiadomość głosowa"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Wprowadź swój klucz przywracania"; "crash_detection_dialog_content" = "%1$@ uległ awarii podczas ostatniego użycia. Czy chcesz przesłać nam raport o awarii?"; "crypto_identity_change_pin_violation" = "Tożsamość %1$@ mogła ulec zmianie. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Aby umożliwić aplikacji korzystanie z aparatu, prosimy o udzielenie zezwolenia w ustawieniach systemowych."; "dialog_permission_generic" = "Proszę nadać uprawnienia w ustawieniach systemowych."; "dialog_permission_location_description_ios" = "Przyznaj dostęp w Ustawienia -> Lokalizacja."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Naciśnij wiadomość i wybierz “%1$@”, aby dołączyć tutaj."; "screen_pinned_timeline_empty_state_headline" = "Przypinaj ważne wiadomości, aby można było je łatwo znaleźć"; "screen_reset_encryption_password_error" = "Wystąpił nieznany błąd. Sprawdź, czy hasło jest poprawne i spróbuj ponownie."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Odblokuj"; "screen_room_member_details_unblock_alert_description" = "Będziesz mógł ponownie zobaczyć wszystkie wiadomości od tego użytkownika."; "screen_room_member_details_unblock_user" = "Odblokuj użytkownika"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Zbanuj"; "screen_room_member_list_ban_member_confirmation_description" = "Nie będą mogli ponownie dołączyć do tego pokoju, jeśli zostaną zaproszeni."; "screen_room_member_list_ban_member_confirmation_title" = "Czy na pewno chcesz zbanować tego członka?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Porównaj liczby"; "screen_session_verification_complete_subtitle" = "Twoja nowa sesja jest teraz zweryfikowana. Ma ona dostęp do Twoich zaszyfrowanych wiadomości, a inni użytkownicy będą widzieć ją jako zaufaną."; "screen_session_verification_enter_recovery_key" = "Wprowadź klucz przywracania"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Udowodnij, że to ty, aby uzyskać dostęp do historii zaszyfrowanych wiadomości."; "screen_session_verification_open_existing_session_title" = "Otwórz istniejącą sesję"; "screen_session_verification_positive_button_canceled" = "Ponów weryfikację"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Oczekiwanie na dopasowanie"; "screen_session_verification_ready_subtitle" = "Porównaj unikalny zestaw emoji."; "screen_session_verification_request_accepted_subtitle" = "Porównaj unikalne emoji, upewniając się, że pojawiły się w tej samej kolejności."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Nie pasują do siebie"; "screen_session_verification_they_match" = "Pasują do siebie"; "screen_session_verification_waiting_to_accept_subtitle" = "Zaakceptuj prośbę o rozpoczęcie procesu weryfikacji w innej sesji, aby kontynuować."; diff --git a/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings b/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings index 7d1e3d7c2d..f14a3de2c2 100644 --- a/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Esqueceu a senha?"; "action_forward" = "Encaminhar"; "action_go_back" = "Voltar"; +"action_ignore" = "Ignore"; "action_invite" = "Convidar"; "action_invite_friends" = "Convidar pessoas"; "action_invite_friends_to_app" = "Convidar pessoas para %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Escuro"; "common_decryption_error" = "Erro de descriptografia"; "common_developer_options" = "Opções do desenvolvedor"; +"common_device_id" = "Device ID"; "common_direct_chat" = "Conversa privada"; "common_edited_suffix" = "(editado)"; "common_editing" = "Editando"; @@ -226,6 +228,8 @@ "common_username" = "Nome do usuário"; "common_verification_cancelled" = "Verificação cancelada"; "common_verification_complete" = "Verificação concluída"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "Verificar dispositivo"; "common_video" = "Vídeo"; "common_voice_message" = "Mensagem de voz"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Insira sua chave de recuperação"; "crash_detection_dialog_content" = "%1$@ fechou inesperadamente na última vez que foi usado. Gostaria de compartilhar um relatório de falhas conosco?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Para permitir que o aplicativo use a câmera, conceda a permissão nas configurações do sistema."; "dialog_permission_generic" = "Por favor, conceda a permissão nas configurações do sistema."; "dialog_permission_location_description_ios" = "Permita o acesso em Configurações -> Localização."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -502,7 +515,7 @@ "screen_invites_empty_list" = "Sem convites"; "screen_invites_invited_you" = "%1$@(%2$@) convidou você"; "screen_join_room_join_action" = "Join room"; -"screen_join_room_knock_action" = "Knock to join"; +"screen_join_room_knock_action" = "Send request to join"; "screen_join_room_space_not_supported_description" = "%1$@ does not support spaces yet. You can access spaces on web."; "screen_join_room_space_not_supported_title" = "Spaces are not supported yet"; "screen_join_room_subtitle_knock" = "Click the button below and a room administrator will be notified. You’ll be able to join the conversation once approved."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Desbloquear"; "screen_room_member_details_unblock_alert_description" = "Você poderá ver todas as mensagens deles novamente."; "screen_room_member_details_unblock_user" = "Desbloquear usuário"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Banir"; "screen_room_member_list_ban_member_confirmation_description" = "They won’t be able to join this room again if invited."; "screen_room_member_list_ban_member_confirmation_title" = "Tem certeza de que quer banir este membro?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Comparar números"; "screen_session_verification_complete_subtitle" = "Sua nova sessão está agora verificada. Ela tem acesso às suas mensagens criptografadas e outros usuários a verão como confiável."; "screen_session_verification_enter_recovery_key" = "Insira a chave de recuperação"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Prove que é você para acessar seu histórico de mensagens criptografadas."; "screen_session_verification_open_existing_session_title" = "Abrir uma sessão existente"; "screen_session_verification_positive_button_canceled" = "Repetir verificação"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Esperando para combinar"; "screen_session_verification_ready_subtitle" = "Compare um conjunto único de emojis."; "screen_session_verification_request_accepted_subtitle" = "Compare os emojis únicos, garantindo que apareçam na mesma ordem."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Eles não combinam"; "screen_session_verification_they_match" = "Eles combinam"; "screen_session_verification_waiting_to_accept_subtitle" = "Aceite a solicitação para iniciar o processo de verificação em sua outra sessão para continuar."; diff --git a/ElementX/Resources/Localizations/pt.lproj/Localizable.strings b/ElementX/Resources/Localizations/pt.lproj/Localizable.strings index 2210564d29..5fd6ff0bfd 100644 --- a/ElementX/Resources/Localizations/pt.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pt.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Esqueceu-se da senha?"; "action_forward" = "Reencaminhar"; "action_go_back" = "Voltar"; +"action_ignore" = "Ignorar"; "action_invite" = "Convidar"; "action_invite_friends" = "Convidar pessoas"; "action_invite_friends_to_app" = "Convidar amigos para %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Escuro"; "common_decryption_error" = "Erro de decifragem"; "common_developer_options" = "Opções de programador"; +"common_device_id" = "ID do dispositivo"; "common_direct_chat" = "Conversa direta"; "common_edited_suffix" = "(editada)"; "common_editing" = "A editar"; @@ -170,7 +172,7 @@ "common_people" = "Pessoas"; "common_permalink" = "Ligação permanente"; "common_permission" = "Permissão"; -"common_please_wait" = "Por favor, aguarda…"; +"common_please_wait" = "Por favor, aguarde…"; "common_poll_end_confirmation" = "Tens a certeza que queres concluir esta sondagem?"; "common_poll_summary" = "Sondagem: %1$@"; "common_poll_total_votes" = "Total de votos: %1$@"; @@ -226,6 +228,8 @@ "common_username" = "Nome de utilizador"; "common_verification_cancelled" = "Verificação cancelada"; "common_verification_complete" = "Verificação concluída"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verificado"; "common_verify_device" = "Verificar o dispositivo"; "common_video" = "Vídeo"; "common_voice_message" = "Mensagem de voz"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Insere a tua chave de recuperação"; "crash_detection_dialog_content" = "A %1$@ teve uma falha da última vez que foi utilizada. Gostarias de partilhar um relatório de acidente connosco?"; "crypto_identity_change_pin_violation" = "A identidade de %1$@ parece ter mudado. %2$@"; +"crypto_identity_change_pin_violation_new" = "A identidade de %1$@ (username: %2$@ ) aparenta ter mudado. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Para que a aplicação possa utilizar a câmara, concede a permissão nas configurações do sistema."; "dialog_permission_generic" = "Concede a permissão nas configurações do sistema."; "dialog_permission_location_description_ios" = "Concede a permissão em Definições -> Localização."; @@ -270,7 +276,7 @@ "error_message_not_found" = "Mensagem não encontrada"; "error_no_compatible_app_found" = "Nenhuma aplicação encontrada capaz de continuar esta ação."; "error_some_messages_have_not_been_sent" = "Algumas mensagens não foram enviadas"; -"error_unknown" = "Ocorreu um erro, desculpa"; +"error_unknown" = "Desculpe, ocorreu um erro"; "event_shield_reason_authenticity_not_guaranteed" = "A autenticidade desta mensagem cifrada não pode ser garantida neste dispositivo."; "event_shield_reason_previously_verified" = "Criptografado por um usuário verificado anteriormente."; "event_shield_reason_sent_in_clear" = "Não cifrado."; @@ -334,11 +340,18 @@ "screen_advanced_settings_element_call_base_url" = "URL base para Element Call personalizado"; "screen_advanced_settings_element_call_base_url_description" = "Define um URL base para a Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL inválido, certifica-te de que incluis o protocolo (http/https) e o endereço correto."; -"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"; -"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; -"screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_create_room_access_section_anyone_option_description" = "Qualquer pessoa pode entrar nesta sala"; +"screen_create_room_access_section_anyone_option_title" = "Qualquer pessoa"; +"screen_create_room_access_section_header" = "Acesso à sala"; +"screen_create_room_access_section_knocking_option_description" = "Qualquer pessoa pode pedir para entrar na sala, mas um administrador ou um moderador terá de aceitar o pedido"; +"screen_create_room_access_section_knocking_option_title" = "Pedir para participar"; +"screen_join_room_cancel_knock_action" = "Cancelar pedido"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Mensagem (opcional)"; +"screen_join_room_knock_sent_description" = "Irá receber um convite para participar na sala se seu pedido for aceite."; +"screen_join_room_knock_sent_title" = "Pedido de adesão enviado"; "screen_pinned_timeline_empty_state_description" = "Pressione uma mensagem e escolha \"%1$@\" para incluir aqui."; "screen_pinned_timeline_empty_state_headline" = "Fixa mensagens importantes para que possam ser facilmente descobertas"; "screen_reset_encryption_password_error" = "Um erro desconhecido aconteceu. Verifique se a senha da sua conta está correta e tente novamente."; @@ -377,7 +390,7 @@ "screen_analytics_prompt_help_us_improve" = "Partilhe dados de utilização anónimos para nos ajudar a identificar problemas."; "screen_analytics_prompt_read_terms" = "Podes ler todos os nossos termos %1$@."; "screen_analytics_prompt_read_terms_content_link" = "aqui"; -"screen_analytics_prompt_settings" = "Podes desligar qualquer momento"; +"screen_analytics_prompt_settings" = "Pode desactivar a qualquer momento"; "screen_analytics_prompt_third_party_sharing" = "Não partilharemos os teus dados com terceiros"; "screen_analytics_prompt_title" = "Ajude a melhorar a %1$@"; "screen_analytics_settings_share_data" = "Partilhar dados de utilização"; @@ -495,9 +508,9 @@ "screen_identity_confirmed_subtitle" = "Agora podes ler ou enviar mensagens de forma segura, e qualquer pessoa com quem converses também pode confiar neste dispositivo."; "screen_identity_confirmed_title" = "Dispositivo verificado"; "screen_identity_waiting_on_other_device" = "A aguardar por outros dispositivos…"; -"screen_invites_decline_chat_message" = "Tens a certeza que queres rejeitar o convite para %1$@?"; -"screen_invites_decline_chat_title" = "Rejeitar conite"; -"screen_invites_decline_direct_chat_message" = "Tens a certeza que queres rejeitar esta conversa privada com %1$@?"; +"screen_invites_decline_chat_message" = "Tens a certeza que queres rejeitar o convite para entra em %1$@?"; +"screen_invites_decline_chat_title" = "Rejeitar convite"; +"screen_invites_decline_direct_chat_message" = "Tem a certeza que queres rejeitar esta conversa privada com %1$@?"; "screen_invites_decline_direct_chat_title" = "Rejeitar conversa"; "screen_invites_empty_list" = "Sem convites"; "screen_invites_invited_you" = "%1$@ (%2$@) convidou-te"; @@ -593,7 +606,7 @@ "screen_qr_code_login_initial_state_item_3" = "Seleciona %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Ligar novo dispositivo”"; "screen_qr_code_login_initial_state_item_4" = "Lê o código QR com este dispositivo"; -"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; +"screen_qr_code_login_initial_state_subtitle" = "Disponível apenas se o seu fornecedor de conta o suportar."; "screen_qr_code_login_initial_state_title" = "Abre a %1$@ noutro dispositivo para obteres o código QR"; "screen_qr_code_login_invalid_scan_state_description" = "Lê o código QR apresentado no outro dispositivo."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Código QR inválido"; @@ -660,7 +673,7 @@ "screen_room_change_permissions_room_name" = "Altera o nome da sala"; "screen_room_change_permissions_room_topic" = "Alterar a descrição da sala"; "screen_room_change_permissions_send_messages" = "Enviar mensagens"; -"screen_room_change_role_administrators_title" = "Editar administradores"; +"screen_room_change_role_administrators_title" = "Editar Administradores"; "screen_room_change_role_confirm_add_admin_description" = "Não poderás desfazer esta ação. Estás a promover o utilizador para ter o mesmo nível de poder que tu."; "screen_room_change_role_confirm_add_admin_title" = "Adicionar administrador?"; "screen_room_change_role_confirm_demote_self_action" = "Despromover"; @@ -668,7 +681,7 @@ "screen_room_change_role_confirm_demote_self_title" = "Despromover-te?"; "screen_room_change_role_invited_member_name" = "%1$@ (pendente)"; "screen_room_change_role_moderators_admin_section_footer" = "Os administradores têm automaticamente privilégios de moderador"; -"screen_room_change_role_moderators_title" = "Editar moderadores"; +"screen_room_change_role_moderators_title" = "Editar Moderadores"; "screen_room_change_role_unsaved_changes_description" = "Tens alterações por guardar."; "screen_room_details_add_topic_title" = "Adicionar descrição"; "screen_room_details_already_a_member" = "Já é participante"; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Desbloquear"; "screen_room_member_details_unblock_alert_description" = "Poderás voltar a ver todas as suas mensagens."; "screen_room_member_details_unblock_user" = "Desbloquear utilizador"; +"screen_room_member_details_verify_button_subtitle" = "Utiliza a aplicação Web para verificar este utilizador."; +"screen_room_member_details_verify_button_title" = "Verifique %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Banir"; "screen_room_member_list_ban_member_confirmation_description" = "Não poderão voltar a entrar nesta sala, mesmo se forem convidados."; "screen_room_member_list_ban_member_confirmation_title" = "Tens a certeza que queres banir este participante?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Comparar números"; "screen_session_verification_complete_subtitle" = "A tua nova sessão está agora verificada, pelo que tem acesso às tuas mensagens cifradas e os outros utilizadores vão vê-la como de confiança."; "screen_session_verification_enter_recovery_key" = "Insere a chave de recuperação"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Prova que és tu para acederes ao teu histórico de mensagens cifradas."; "screen_session_verification_open_existing_session_title" = "Abrir sessão existente"; "screen_session_verification_positive_button_canceled" = "Repetir verificação"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "A aguardar correspondência"; "screen_session_verification_ready_subtitle" = "Compara um conjunto único de emojis."; "screen_session_verification_request_accepted_subtitle" = "Compara os emojis únicos, certificando-te de que aparecem pela mesma ordem."; +"screen_session_verification_request_details_timestamp" = "Sessão iniciada"; +"screen_session_verification_request_footer" = "Continue apenas se tiver iniciado esta verificação."; +"screen_session_verification_request_subtitle" = "Verifique o outro dispositivo para manter o histórico de mensagens seguro."; +"screen_session_verification_request_title" = "Verificação solicitada"; "screen_session_verification_they_dont_match" = "Não correspondem"; "screen_session_verification_they_match" = "Correspondem"; "screen_session_verification_waiting_to_accept_subtitle" = "Para continuar, aceita o pedido de verificação na tua outra sessão."; diff --git a/ElementX/Resources/Localizations/ro.lproj/Localizable.strings b/ElementX/Resources/Localizations/ro.lproj/Localizable.strings index 2e86193909..633f7bf355 100644 --- a/ElementX/Resources/Localizations/ro.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ro.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Ați uitat parola?"; "action_forward" = "Redirecționați"; "action_go_back" = "Înapoi"; +"action_ignore" = "Ignore"; "action_invite" = "Invitați"; "action_invite_friends" = "Invitați prieteni"; "action_invite_friends_to_app" = "Invitați prieteni în %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Întunecat"; "common_decryption_error" = "Eroare de decriptare"; "common_developer_options" = "Opțiuni programator"; +"common_device_id" = "Device ID"; "common_direct_chat" = "Chat direct"; "common_edited_suffix" = "(editat)"; "common_editing" = "Editare"; @@ -226,6 +228,8 @@ "common_username" = "Utilizator"; "common_verification_cancelled" = "Verificare anulată"; "common_verification_complete" = "Verificare completă"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "Verificați dispozitivul"; "common_video" = "Video"; "common_voice_message" = "Mesaj vocal"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Confirmați cheia de recuperare"; "crash_detection_dialog_content" = "%1$@ s-a blocat ultima dată când a fost folosit. Doriți să ne trimiteți un raport?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Pentru a permite aplicației să utilizeze camera, vă rugăm să acordați permisiunea în setările sistemului."; "dialog_permission_generic" = "Vă rugăm să acordați permisiunea în setările sistemului."; "dialog_permission_location_description_ios" = "Acordați accesul în Setări -> Locație."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Deblocați"; "screen_room_member_details_unblock_alert_description" = "La deblocarea utilizatorului, veți putea vedea din nou toate mesajele de la acesta."; "screen_room_member_details_unblock_user" = "Deblocați utilizatorul"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Interzicere"; "screen_room_member_list_ban_member_confirmation_description" = "Nu se vor putea alătura din nou acestei camere dacă sunt invitați."; "screen_room_member_list_ban_member_confirmation_title" = "Sunteți sigur că doriți să interziceți acest membru?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Comparați numerele"; "screen_session_verification_complete_subtitle" = "Noua dumneavoastră sesiune este acum verificată. Are acces la mesajele dumneavoastră criptate, iar alți utilizatori vă vor vedea ca fiind de încredere."; "screen_session_verification_enter_recovery_key" = "Introduceți cheia de recuperare"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Demonstrați-vă identitatea pentru a accesa istoricul mesajelor criptate."; "screen_session_verification_open_existing_session_title" = "Deschideți o sesiune existentă"; "screen_session_verification_positive_button_canceled" = "Reîncercați verificarea"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Se așteaptă confirmarea"; "screen_session_verification_ready_subtitle" = "Comparați un set unic de emoji-uri."; "screen_session_verification_request_accepted_subtitle" = "Comparăți emoticoalene asigurându-vă că apar în aceeași ordine."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Nu se potrivesc"; "screen_session_verification_they_match" = "Se potrivesc"; "screen_session_verification_waiting_to_accept_subtitle" = "Acceptați solicitarea de a începe procesul de verificare în cealaltă sesiune pentru a continua."; diff --git a/ElementX/Resources/Localizations/ru.lproj/Localizable.strings b/ElementX/Resources/Localizations/ru.lproj/Localizable.strings index d9d0f216f4..127b6df9c7 100644 --- a/ElementX/Resources/Localizations/ru.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ru.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Забыли пароль?"; "action_forward" = "Переслать"; "action_go_back" = "Вернуться"; +"action_ignore" = "Игнорировать"; "action_invite" = "Пригласить"; "action_invite_friends" = "Пригласить в комнату"; "action_invite_friends_to_app" = "Пригласить в %1$@"; @@ -61,7 +62,7 @@ "action_invites_list" = "Приглашения"; "action_join" = "Присоединиться"; "action_learn_more" = "Подробнее"; -"action_leave" = "Выйти"; +"action_leave" = "Покинуть"; "action_leave_conversation" = "Покинуть беседу"; "action_leave_room" = "Покинуть комнату"; "action_load_more" = "Загрузить еще"; @@ -94,7 +95,7 @@ "action_send_message" = "Отправить сообщение"; "action_share" = "Поделиться"; "action_share_link" = "Поделиться ссылкой"; -"action_show" = "Show"; +"action_show" = "Показать"; "action_sign_in_again" = "Повторите вход"; "action_signout" = "Выйти"; "action_signout_anyway" = "Все равно выйти"; @@ -111,8 +112,8 @@ "action_view_source" = "Показать источник"; "action_yes" = "Да"; "banner_migrate_to_native_sliding_sync_action" = "Выйти и обновить"; -"banner_migrate_to_native_sliding_sync_description" = "Теперь ваш сервер поддерживает новый, более быстрый протокол. Выйдите из системы и снова войдите в систему для обновления прямо сейчас. Сделав это сейчас, вы сможете избежать принудительного выхода из системы при последующем удалении старого протокола."; -"banner_migrate_to_native_sliding_sync_force_logout_title" = "Ваш homeserver больше не поддерживает старый протокол. Пожалуйста, выйдите из системы и войдите снова, чтобы продолжить использование приложения."; +"banner_migrate_to_native_sliding_sync_description" = "Теперь ваш сервер поддерживает новый, более быстрый протокол. Чтобы обновить его прямо сейчас, выйдите и войдите в свою учётную запись снова. Сделав это сейчас, вы сможете избежать принудительного выхода из системы при последующем удалении старого протокола."; +"banner_migrate_to_native_sliding_sync_force_logout_title" = "Ваш домашний сервер больше не поддерживает старый протокол. Пожалуйста, выйдите и войдите в свою учётную запись снова, чтобы продолжить использование приложения."; "banner_migrate_to_native_sliding_sync_title" = "Доступно обновление"; "banner.set_up_recovery.content" = "Создайте новый ключ восстановления, который можно использовать для восстановления зашифрованной истории сообщений в случае потери доступа к своим устройствам."; "banner.set_up_recovery.title" = "Настроить восстановление"; @@ -130,9 +131,10 @@ "common_copyright" = "Авторское право"; "common_creating_room" = "Создание комнаты…"; "common_current_user_left_room" = "Покинул комнату"; -"common_dark" = "Темная"; +"common_dark" = "Тёмное"; "common_decryption_error" = "Ошибка расшифровки"; "common_developer_options" = "Для разработчика"; +"common_device_id" = "Идентификатор устройства"; "common_direct_chat" = "Личный чат"; "common_edited_suffix" = "(изменено)"; "common_editing" = "Редактирование"; @@ -140,7 +142,7 @@ "common_encryption_enabled" = "Шифрование включено"; "common_enter_your_pin" = "Введите свой PIN-код"; "common_error" = "Ошибка"; -"common_everyone" = "Для всех"; +"common_everyone" = "Все"; "common_face_id_ios" = "Face ID"; "common_failed" = "Ошибка"; "common_favourite" = "Избранное"; @@ -151,8 +153,8 @@ "common_image" = "Изображения"; "common_in_reply_to" = "В ответ на %1$@"; "common_invite_unknown_profile" = "Идентификатор Matrix ID не найден, приглашение может быть не получено."; -"common_leaving_room" = "Покинуть комнату"; -"common_light" = "Светлая"; +"common_leaving_room" = "Покидание комнаты"; +"common_light" = "Светлое"; "common_link_copied_to_clipboard" = "Ссылка скопирована в буфер обмена"; "common_loading" = "Загрузка…"; "common_message" = "Сообщение"; @@ -160,9 +162,9 @@ "common_message_layout" = "Оформление сообщения"; "common_message_removed" = "Сообщение удалено"; "common_modern" = "Современный"; -"common_mute" = "Без звука"; +"common_mute" = "Выкл. звук"; "common_no_results" = "Ничего не найдено"; -"common_no_room_name" = "Нету названия комнаты"; +"common_no_room_name" = "Название комнаты отсутствует"; "common_offline" = "Не в сети"; "common_optic_id_ios" = "Оптический идентификатор"; "common_or" = "или"; @@ -187,11 +189,11 @@ "common_rich_text_editor" = "Редактор форматированного текста"; "common_room" = "Комната"; "common_room_name" = "Название комнаты"; -"common_room_name_placeholder" = "напр., название вашего проекта"; -"common_saved_changes" = "Сохраненные изменения"; +"common_room_name_placeholder" = "например, название вашего проекта"; +"common_saved_changes" = "Изменения сохранены"; "common_saving" = "Сохранение"; -"common_screen_lock" = "Блокировка экрана"; -"common_search_for_someone" = "Поиск человека"; +"common_screen_lock" = "Блокировка приложения"; +"common_search_for_someone" = "Найти кого-нибудь"; "common_search_results" = "Результаты поиска"; "common_security" = "Безопасность"; "common_seen_by" = "Просмотрено"; @@ -201,15 +203,15 @@ "common_server_not_supported" = "Сервер не поддерживается"; "common_server_url" = "Адрес сервера"; "common_settings" = "Настройки"; -"common_shared_location" = "Делится местонахождением"; +"common_shared_location" = "Поделился местоположением"; "common_signing_out" = "Выход…"; "common_something_went_wrong" = "Что-то пошло не так"; -"common_starting_chat" = "Начало чата…"; +"common_starting_chat" = "Чат запускается…"; "common_sticker" = "Стикер"; "common_success" = "Успешно"; "common_suggestions" = "Предложения"; "common_syncing" = "Синхронизация"; -"common_system" = "Системная"; +"common_system" = "Системное"; "common_text" = "Текст"; "common_third_party_notices" = "Уведомление о третьей стороне"; "common_thread" = "Обсуждение"; @@ -221,26 +223,30 @@ "common_unable_to_invite_message" = "Не удалось отправить приглашения одному или нескольким пользователям."; "common_unable_to_invite_title" = "Не удалось отправить приглашение(я)"; "common_unlock" = "Разблокировать"; -"common_unmute" = "Включить звук"; +"common_unmute" = "Вкл. звук"; "common_unsupported_event" = "Неподдерживаемое событие"; "common_username" = "Имя пользователя"; "common_verification_cancelled" = "Проверка отменена"; "common_verification_complete" = "Проверка завершена"; +"common_verification_failed" = "Сбой проверки"; +"common_verified" = "Проверено"; "common_verify_device" = "Подтверждение устройства"; "common_video" = "Видео"; "common_voice_message" = "Голосовое сообщение"; "common_waiting" = "Ожидание…"; "common_waiting_for_decryption_key" = "Ожидание ключа расшифровки"; -"common.copied_to_clipboard" = "Copied to clipboard"; +"common.copied_to_clipboard" = "Скопировано в буфер обмена"; "common.do_not_show_this_again" = "Не показывать больше"; "common.open_source_licenses" = "Лицензии с открытым исходным кодом"; "common.pinned" = "Закрепленный"; "common.send_to" = "Отправить"; -"common.you" = "You"; -"confirm_recovery_key_banner_message" = "В настоящее время резервная копия вашего чата не синхронизирована. Требуется подтвердить вашим ключом восстановления, чтобы сохранить доступ к резервной копии чата."; +"common.you" = "Вы"; +"confirm_recovery_key_banner_message" = "В настоящее время резервная копия ваших чатов не синхронизирована. Вам потребуется ввести свой ключ восстановления, чтобы сохранить доступ к резервной копии чатов."; "confirm_recovery_key_banner_title" = "Введите ключ восстановления"; "crash_detection_dialog_content" = "При последнем использовании %1$@ произошел сбой. Хотите поделиться отчетом о сбое?"; -"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation" = "Судя по всему, идентификатор %1$@ изменился. %2$@"; +"crypto_identity_change_pin_violation_new" = "Пользователь %1$@ сменил имя пользователя на %2$@. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Чтобы приложение могло использовать камеру, предоставьте разрешение в системных настройках."; "dialog_permission_generic" = "Пожалуйста, предоставьте разрешение в системных настройках."; "dialog_permission_location_description_ios" = "Предоставьте доступ в меню «Настройки» -> «Местоположение»."; @@ -261,7 +267,7 @@ "emoji_picker_category_people" = "Улыбки и люди"; "emoji_picker_category_places" = "Путешествия и места"; "emoji_picker_category_symbols" = "Символы"; -"error_account_creation_not_possible" = "Ваш homeserver необходимо обновить, чтобы он поддерживал Matrix Authentication Service и создание учетной записи."; +"error_account_creation_not_possible" = "Ваш домашний сервер необходимо обновить, чтобы он поддерживал Matrix Authentication Service и создание учётных записей."; "error_failed_creating_the_permalink" = "Не удалось создать постоянную ссылку"; "error_failed_loading_map" = "Не удалось загрузить карту %1$@. Пожалуйста, повторите попытку позже."; "error_failed_loading_messages" = "Не удалось загрузить сообщения"; @@ -277,13 +283,13 @@ "event_shield_reason_unknown_device" = "Зашифровано неизвестным или удаленным устройством."; "event_shield_reason_unsigned_device" = "Зашифровано устройством, не проверенным его владельцем."; "event_shield_reason_unverified_identity" = "Зашифровано непроверенным пользователем."; -"full_screen_intent_banner_message" = "Чтобы никогда не пропустить важный звонок, измените настройки, чтобы разрешить полноэкранные уведомления, когда ваш телефон заблокирован."; +"full_screen_intent_banner_message" = "Чтобы больше не пропускать важные звонки, разрешите приложению показывать полноэкранные уведомления на заблокированном экране телефона."; "full_screen_intent_banner_title" = "Улучшите качество звонков"; "invite_friends_rich_title" = "🔐️ Присоединяйтесь ко мне в %1$@"; "invite_friends_text" = "Привет, поговори со мной по %1$@: %2$@"; -"leave_conversation_alert_subtitle" = "Вы уверены, что хотите покинуть беседу?"; +"leave_conversation_alert_subtitle" = "Вы уверены, что хотите покинуть беседу? Эта беседа не является общедоступной, и Вы не сможете присоединиться к ней без приглашения."; "leave_room_alert_empty_subtitle" = "Вы уверены, что хотите покинуть эту комнату? Вы здесь единственный человек. Если вы уйдете, никто не сможет присоединиться в будущем, включая вас."; -"leave_room_alert_private_subtitle" = "Вы уверены, что хотите покинуть эту комнату? Эта комната не является публичной, и Вы не сможете присоединиться к ней без приглашения."; +"leave_room_alert_private_subtitle" = "Вы уверены, что хотите покинуть эту комнату? Эта комната не является общедоступной, и Вы не сможете присоединиться к ней без приглашения."; "leave_room_alert_subtitle" = "Вы уверены, что хотите покинуть комнату?"; "login_initial_device_name_ios" = "%1$@ iOS"; "notification_channel_call" = "Позвонить"; @@ -334,11 +340,18 @@ "screen_advanced_settings_element_call_base_url" = "Базовый URL сервера звонков Element"; "screen_advanced_settings_element_call_base_url_description" = "Задайте свой сервер Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Адрес указан неверно, удостоверьтесь, что вы указали протокол (http/https) и правильный адрес."; -"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"; -"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; -"screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_create_room_access_section_anyone_option_description" = "Любой желающий может присоединиться к этой комнате"; +"screen_create_room_access_section_anyone_option_title" = "Любой"; +"screen_create_room_access_section_header" = "Доступ в комнату"; +"screen_create_room_access_section_knocking_option_description" = "Любой желающий может подать заявку на присоединение к комнате, но администратор или модератор должен будет принять запрос."; +"screen_create_room_access_section_knocking_option_title" = "Попросить присоединиться"; +"screen_join_room_cancel_knock_action" = "Отменить запрос"; +"screen_join_room_cancel_knock_alert_confirmation" = "Да, отменить"; +"screen_join_room_cancel_knock_alert_description" = "Вы действительно хотите отменить заявку на вступление в эту комнату?"; +"screen_join_room_cancel_knock_alert_title" = "Отменить запрос на присоединение"; +"screen_join_room_knock_message_description" = "Сообщение (опционально)"; +"screen_join_room_knock_sent_description" = "Вы получите приглашение присоединиться к комнате, как только ваш запрос будет принят."; +"screen_join_room_knock_sent_title" = "Запрос на присоединение отправлен"; "screen_pinned_timeline_empty_state_description" = "Нажмите на сообщение и выберите “%1$@”, чтобы добавить его сюда."; "screen_pinned_timeline_empty_state_headline" = "Закрепите важные сообщения, чтобы их можно было легко найти"; "screen_reset_encryption_password_error" = "Произошла неизвестная ошибка. Проверьте правильность пароля учетной записи и повторите попытку."; @@ -374,18 +387,18 @@ "screen_advanced_settings_share_presence_description" = "Если выключено, вы не сможете отправлять, получать уведомления о прочтении и наборе текста"; "screen_advanced_settings_view_source_description" = "Включить опцию просмотра источника сообщения в ленте."; "screen_analytics_prompt_data_usage" = "Мы не будем записывать или профилировать какие-либо персональные данные"; -"screen_analytics_prompt_help_us_improve" = "Предоставлять анонимные данные об использовании, чтобы помочь нам выявить проблемы."; +"screen_analytics_prompt_help_us_improve" = "Предоставьте разработчикам анонимные данные об использовании, чтобы помочь им выявлять проблемы эффективнее."; "screen_analytics_prompt_read_terms" = "Вы можете ознакомиться со всеми нашими условиями %1$@."; "screen_analytics_prompt_read_terms_content_link" = "здесь"; "screen_analytics_prompt_settings" = "Вы можете отключить эту функцию в любое время"; "screen_analytics_prompt_third_party_sharing" = "Мы не будем передавать ваши данные третьим лицам"; "screen_analytics_prompt_title" = "Помогите улучшить %1$@"; -"screen_analytics_settings_share_data" = "Делитесь данными аналитики"; +"screen_analytics_settings_share_data" = "Отправлять аналитические данные"; "screen_app_lock_biometric_authentication" = "биометрическая идентификация"; "screen_app_lock_biometric_unlock" = "биометрическая разблокировка"; "screen_app_lock_biometric_unlock_reason_ios" = "Для доступа к приложению необходима аутентификация"; "screen_app_lock_forgot_pin" = "Забыли PIN-код?"; -"screen_app_lock_settings_change_pin" = "Измените PIN-код"; +"screen_app_lock_settings_change_pin" = "Изменить PIN-код"; "screen_app_lock_settings_enable_biometric_unlock" = "Разрешить биометрическую разблокировку"; "screen_app_lock_settings_enable_face_id_ios" = "Разрешить Face ID"; "screen_app_lock_settings_enable_optic_id_ios" = "Разрешить Optic ID"; @@ -404,7 +417,7 @@ "screen_app_lock_setup_pin_mismatch_dialog_content" = "Повторите PIN-код"; "screen_app_lock_setup_pin_mismatch_dialog_title" = "PIN-коды не совпадают"; "screen_app_lock_signout_alert_message" = "Чтобы продолжить, вам необходимо повторно войти в систему и создать новый PIN-код"; -"screen_app_lock_signout_alert_title" = "Вы выходите из системы"; +"screen_app_lock_signout_alert_title" = "Выполняется выход из системы"; "screen_blocked_users_empty" = "У вас нет заблокированных пользователей"; "screen_blocked_users_unblocking" = "Разблокировка…"; "screen_bug_report_attach_screenshot" = "Приложить снимок экрана"; @@ -436,11 +449,11 @@ "screen_chat_backup_key_backup_description" = "Резервное копирование гарантирует, что вы не потеряете историю сообщений. %1$@."; "screen_chat_backup_key_backup_title" = "Резервное копирование"; "screen_chat_backup_recovery_action_change" = "Изменить ключ восстановления"; -"screen_chat_backup_recovery_action_confirm_description" = "Резервная копия чата в настоящее время не синхронизирована."; +"screen_chat_backup_recovery_action_confirm_description" = "В настоящее время резервная копия ваших чатов не синхронизирована."; "screen_chat_backup_recovery_action_setup" = "Настроить восстановление"; "screen_chat_backup_recovery_action_setup_description" = "Получите доступ к зашифрованным сообщениям, если вы потеряете все свои устройства или выйдете из системы %1$@ отовсюду."; "screen_create_account_title" = "Создать учетную запись"; -"screen_create_new_recovery_key_list_item_1" = "Откройте %1$@ на настольном устройстве"; +"screen_create_new_recovery_key_list_item_1" = "Откройте %1$@ на компьютере"; "screen_create_new_recovery_key_list_item_2" = "Войдите в свой аккаунт еще раз"; "screen_create_new_recovery_key_list_item_3" = "Когда вас попросят подтвердить устройство, выберите %1$@"; "screen_create_new_recovery_key_list_item_3_reset_all" = "“Сбросить все”"; @@ -449,18 +462,18 @@ "screen_create_new_recovery_key_title" = "Сбросьте шифрование вашей учетной записи с помощью другого устройства."; "screen_create_poll_add_option_btn" = "Добавить вариант"; "screen_create_poll_anonymous_desc" = "Показывать результаты только после окончания опроса"; -"screen_create_poll_anonymous_headline" = "Анонимный опрос"; -"screen_create_poll_answer_hint" = "Настройка %1$d"; +"screen_create_poll_anonymous_headline" = "Скрыть голоса"; +"screen_create_poll_answer_hint" = "Вариант %1$d"; "screen_create_poll_cancel_confirmation_title_ios" = "Отменить опрос"; "screen_create_poll_question_desc" = "Вопрос или тема"; -"screen_create_poll_question_hint" = "Тема опроса?"; +"screen_create_poll_question_hint" = "О чём будет опрос?"; "screen_create_poll_title" = "Создать опрос"; -"screen_create_room_action_create_room" = "Новая комната"; +"screen_create_room_action_create_room" = "Создать новую комнату"; "screen_create_room_error_creating_room" = "Произошла ошибка при создании комнаты"; -"screen_create_room_private_option_description" = "Сообщения в этой комнате зашифрованы. Отключить шифрование позже будет невозможно."; -"screen_create_room_private_option_title" = "Приватная комната (только по приглашению)"; -"screen_create_room_public_option_description" = "Сообщения не зашифрованы, каждый может их прочитать. Вы можете включить шифрование позже."; -"screen_create_room_public_option_title" = "Публичная комната (любой)"; +"screen_create_room_private_option_description" = "Сообщения в этой комнате будут зашифрованы. Отключить шифрование позже будет невозможно."; +"screen_create_room_private_option_title" = "Частная комната (только по приглашениям)"; +"screen_create_room_public_option_description" = "Сообщения не будут зашифрованы и каждый сможет их прочитать. Шифрование можно будет включить позже."; +"screen_create_room_public_option_title" = "Общедоступная комната (для всех)"; "screen_create_room_topic_label" = "Тема (необязательно)"; "screen_deactivate_account_confirmation_dialog_content" = "Подтвердите, что вы хотите деактивировать свою учетную запись. Это действие не может быть отменено."; "screen_deactivate_account_delete_all_messages" = "Удалить все мои сообщения"; @@ -521,7 +534,7 @@ "screen_login_error_invalid_user_id" = "Это не корректный идентификатор пользователя. Ожидаемый формат: '@user:homeserver.org'"; "screen_login_error_refresh_tokens" = "Этот сервер настроен на использование токенов обновления. Они не поддерживаются при использовании входа на основе пароля."; "screen_login_error_unsupported_authentication" = "Выбранный домашний сервер не поддерживает пароль или логин OIDC. Пожалуйста, свяжитесь с администратором или выберите другой домашний сервер."; -"screen_login_form_header" = "Введите сведения о себе"; +"screen_login_form_header" = "Введите свои данные"; "screen_login_title" = "Рады видеть вас снова!"; "screen_login_title_with_homeserver" = "Войти в %1$@"; "screen_media_picker_error_failed_selection" = "Не удалось выбрать носитель, попробуйте еще раз."; @@ -530,46 +543,46 @@ "screen_migration_message" = "Это одноразовый процесс, спасибо, что подождали."; "screen_migration_title" = "Настройка учетной записи."; "screen_notification_optin_subtitle" = "Вы можете изменить настройки позже."; -"screen_notification_optin_title" = "Разрешите уведомления и никогда не пропустите сообщение"; +"screen_notification_optin_title" = "Разрешите отправку уведомлений и ни одно сообщение не будет пропущено"; "screen_notification_settings_additional_settings_section_title" = "Дополнительные параметры"; "screen_notification_settings_calls_label" = "Аудио и видео звонки"; "screen_notification_settings_configuration_mismatch" = "Несоответствие конфигурации"; "screen_notification_settings_configuration_mismatch_description" = "Мы упростили настройки уведомлений, чтобы упростить поиск опций. Некоторые пользовательские настройки, выбранные вами ранее, не отображаются в данном меню, но они все еще активны. \n\nЕсли вы продолжите, некоторые настройки могут быть изменены."; -"screen_notification_settings_direct_chats" = "Прямые чаты"; +"screen_notification_settings_direct_chats" = "В личных чатах"; "screen_notification_settings_edit_custom_settings_section_title" = "Персональные настройки для каждого чата"; -"screen_notification_settings_edit_failed_updating_default_mode" = "При обновлении настроек уведомления произошла ошибка."; -"screen_notification_settings_edit_mode_all_messages" = "Все сообщения"; +"screen_notification_settings_edit_failed_updating_default_mode" = "Произошла ошибка при обновлении настройки уведомления."; +"screen_notification_settings_edit_mode_all_messages" = "О всех сообщениях"; "screen_notification_settings_edit_mode_mentions_and_keywords" = "Только упоминания и ключевые слова"; "screen_notification_settings_edit_screen_direct_section_header" = "Уведомлять меня в личных чатах"; "screen_notification_settings_edit_screen_group_section_header" = "Уведомлять меня в групповых чатах"; "screen_notification_settings_enable_notifications" = "Включить уведомления на данном устройстве"; "screen_notification_settings_failed_fixing_configuration" = "Конфигурация не была исправлена, попробуйте еще раз."; -"screen_notification_settings_group_chats" = "Групповые чаты"; +"screen_notification_settings_group_chats" = "В групповых чатах"; "screen_notification_settings_invite_for_me_label" = "Приглашения"; "screen_notification_settings_mentions_only_disclaimer" = "Ваш домашний сервер не поддерживает эту опцию в зашифрованных комнатах, в некоторых комнатах вы можете не получать уведомления."; "screen_notification_settings_mode_all" = "Все"; "screen_notification_settings_mode_mentions" = "Упоминания"; -"screen_notification_settings_notification_section_title" = "Уведомить меня"; -"screen_notification_settings_room_mention_label" = "Уведомить меня в @room"; +"screen_notification_settings_notification_section_title" = "Уведомлять меня"; +"screen_notification_settings_room_mention_label" = "Уведомлять меня при упоминании @room"; "screen_notification_settings_system_notifications_action_required" = "Чтобы получать уведомления, измените свой %1$@."; "screen_notification_settings_system_notifications_action_required_content_link" = "настройки системы"; "screen_notification_settings_system_notifications_turned_off" = "Системные уведомления выключены"; "screen_notification_settings_title" = "Уведомления"; -"screen_onboarding_sign_in_manually" = "Вход в систему вручную"; -"screen_onboarding_sign_in_with_qr_code" = "Войти с помощью QR-кода"; +"screen_onboarding_sign_in_manually" = "Войти вручную"; +"screen_onboarding_sign_in_with_qr_code" = "Войти QR-кодом"; "screen_onboarding_sign_up" = "Создать учетную запись"; -"screen_onboarding_welcome_message" = "Добро пожаловать в самый быстрый %1$@. Сверхзаряженность на скорость и простоту."; -"screen_onboarding_welcome_subtitle" = "Добро пожаловать в %1$@. Сверхзаряжен для скорости и простоты."; -"screen_onboarding_welcome_title" = "Будьте в своем element"; +"screen_onboarding_welcome_message" = "Добро пожаловать в самый быстрый клиент %1$@. Ориентирован на скорость и простоту."; +"screen_onboarding_welcome_subtitle" = "Добро пожаловать в %1$@. Ориентирован на скорость и простоту."; +"screen_onboarding_welcome_title" = "Чувствуйте себя как дома с Element"; "screen_polls_history_empty_ongoing" = "Не найдено текущих опросов."; -"screen_polls_history_empty_past" = "Не найдено предыдущих опросов."; +"screen_polls_history_empty_past" = "Не найдено прошлых опросов."; "screen_polls_history_filter_ongoing" = "Текущие"; "screen_polls_history_filter_past" = "Прошлые"; "screen_polls_history_title" = "Опросы"; "screen_qr_code_login_connecting_subtitle" = "Установление безопасного соединения"; "screen_qr_code_login_connection_note_secure_state_description" = "Не удалось установить безопасное соединение с новым устройством. Существующие устройства по-прежнему в безопасности, и вам не нужно беспокоиться о них."; "screen_qr_code_login_connection_note_secure_state_list_header" = "Что теперь?"; -"screen_qr_code_login_connection_note_secure_state_list_item_1" = "Попробуйте снова войти в систему с помощью QR-кода, если это была сетевая проблема"; +"screen_qr_code_login_connection_note_secure_state_list_item_1" = "Попробуйте снова войти в систему с помощью QR-кода, если это была проблема с соединением"; "screen_qr_code_login_connection_note_secure_state_list_item_2" = "Если вы столкнулись с той же проблемой, попробуйте сменить точку доступа Wi-Fi или используйте мобильные данные"; "screen_qr_code_login_connection_note_secure_state_list_item_3" = "Если это не помогло, войдите вручную"; "screen_qr_code_login_connection_note_secure_state_title" = "Соединение не защищено"; @@ -588,12 +601,12 @@ "screen_qr_code_login_error_sliding_sync_not_supported_subtitle" = "Поставщик учетной записи не поддерживает %1$@."; "screen_qr_code_login_error_sliding_sync_not_supported_title" = "%1$@ не поддерживается"; "screen_qr_code_login_initial_state_button_title" = "Готово к сканированию"; -"screen_qr_code_login_initial_state_item_1" = "Откройте %1$@ на настольном устройстве"; +"screen_qr_code_login_initial_state_item_1" = "Откройте %1$@ на компьютере"; "screen_qr_code_login_initial_state_item_2" = "Нажмите на свое изображение"; -"screen_qr_code_login_initial_state_item_3" = "Выбрать %1$@"; +"screen_qr_code_login_initial_state_item_3" = "Выберите %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "\"Привязать новое устройство\""; "screen_qr_code_login_initial_state_item_4" = "Отсканируйте QR-код с помощью этого устройства"; -"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; +"screen_qr_code_login_initial_state_subtitle" = "Доступно только в том случае, если ваш поставщик учетной записи поддерживает это."; "screen_qr_code_login_initial_state_title" = "Откройте %1$@ на другом устройстве, чтобы получить QR-код"; "screen_qr_code_login_invalid_scan_state_description" = "Используйте QR-код, показанный на другом устройстве."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Неверный QR-код"; @@ -650,16 +663,16 @@ "screen_room_attachment_source_location" = "Местоположение"; "screen_room_attachment_source_poll" = "Опрос"; "screen_room_attachment_text_formatting" = "Форматирование текста"; -"screen_room_change_permissions_administrators" = "Только для администраторов"; -"screen_room_change_permissions_ban_people" = "Заблокировать людей"; -"screen_room_change_permissions_delete_messages" = "Удалить сообщения"; -"screen_room_change_permissions_invite_people" = "Пригласить людей"; +"screen_room_change_permissions_administrators" = "Только администраторы"; +"screen_room_change_permissions_ban_people" = "Блокировать людей могут"; +"screen_room_change_permissions_delete_messages" = "Удалять сообщения могут"; +"screen_room_change_permissions_invite_people" = "Приглашать людей могут"; "screen_room_change_permissions_moderators" = "Администраторы и модераторы"; -"screen_room_change_permissions_remove_people" = "Удалить людей"; -"screen_room_change_permissions_room_avatar" = "Изменить изображение комнаты"; -"screen_room_change_permissions_room_name" = "Изменить название комнаты"; -"screen_room_change_permissions_room_topic" = "Сменить тему комнаты"; -"screen_room_change_permissions_send_messages" = "Отправить сообщение"; +"screen_room_change_permissions_remove_people" = "Удалять людей могут"; +"screen_room_change_permissions_room_avatar" = "Менять изображение комнаты могут"; +"screen_room_change_permissions_room_name" = "Менять название комнаты могут"; +"screen_room_change_permissions_room_topic" = "Менять тему комнаты могут"; +"screen_room_change_permissions_send_messages" = "Отправлять сообщения могут"; "screen_room_change_role_administrators_title" = "Редактировать роль администраторов"; "screen_room_change_role_confirm_add_admin_description" = "Вы не сможете отменить это действие. Вы устанавливаете уровень пользователю соответствующий вашему."; "screen_room_change_role_confirm_add_admin_title" = "Добавить администратора?"; @@ -674,14 +687,14 @@ "screen_room_details_already_a_member" = "Уже зарегистрирован"; "screen_room_details_already_invited" = "Уже приглашены"; "screen_room_details_badge_encrypted" = "Зашифровано"; -"screen_room_details_badge_not_encrypted" = "Не зашифровано"; -"screen_room_details_badge_public" = "Общественная комната"; +"screen_room_details_badge_not_encrypted" = "Шифрования нет"; +"screen_room_details_badge_public" = "Общедоступная комната"; "screen_room_details_edit_room_title" = "Редактировать комнату"; "screen_room_details_edition_error" = "Произошла неизвестная ошибка и информацию не удалось изменить."; "screen_room_details_edition_error_title" = "Не удалось обновить комнату"; "screen_room_details_encryption_enabled_subtitle" = "Сообщения зашифрованы. Только у вас и у получателей есть уникальные ключи для их разблокировки."; "screen_room_details_encryption_enabled_title" = "Шифрование сообщений включено"; -"screen_room_details_error_loading_notification_settings" = "При загрузке настроек уведомлений произошла ошибка."; +"screen_room_details_error_loading_notification_settings" = "Произошла ошибка при загрузке настроек уведомлений."; "screen_room_details_error_muting" = "Не удалось отключить звук в этой комнате, попробуйте еще раз."; "screen_room_details_error_unmuting" = "Не удалось включить звук в эту комнату, попробуйте еще раз."; "screen_room_details_notification_mode_custom" = "Пользовательский"; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Разблокировать"; "screen_room_member_details_unblock_alert_description" = "Вы снова сможете увидеть все сообщения."; "screen_room_member_details_unblock_user" = "Разблокировать пользователя"; +"screen_room_member_details_verify_button_subtitle" = "Используйте веб-приложение для проверки этого пользователя."; +"screen_room_member_details_verify_button_title" = "Верифицировать %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Заблокировать"; "screen_room_member_list_ban_member_confirmation_description" = "Они не смогут снова присоединиться к этой комнате, если их пригласят."; "screen_room_member_list_ban_member_confirmation_title" = "Вы уверены, что хотите заблокировать этого участника?"; @@ -716,7 +731,7 @@ "screen_room_member_list_manage_member_unban_message" = "Они снова смогут присоединиться в эту комнату если их пригласят."; "screen_room_member_list_manage_member_unban_title" = "Разбанить пользователя?"; "screen_room_member_list_manage_member_user_info" = "Посмотреть профиль"; -"screen_room_member_list_mode_banned" = "Заблокирован"; +"screen_room_member_list_mode_banned" = "Заблокированные"; "screen_room_member_list_mode_members" = "Участники"; "screen_room_member_list_pending_header_title" = "В ожидании"; "screen_room_member_list_removing_user" = "Удаление %1$@…"; @@ -726,7 +741,7 @@ "screen_room_member_list_unbanning_user" = "Разблокировка %1$@"; "screen_room_notification_settings_allow_custom" = "Разрешить пользовательские настройки"; "screen_room_notification_settings_allow_custom_footnote" = "Включение этого параметра отменяет настройки по умолчанию"; -"screen_room_notification_settings_custom_settings_title" = "Уведомить меня в этом чате"; +"screen_room_notification_settings_custom_settings_title" = "Уведомлять меня в этом чате"; "screen_room_notification_settings_default_setting_footnote" = "Вы можете изменить его в своем %1$@."; "screen_room_notification_settings_default_setting_footnote_content_link" = "основные настройки"; "screen_room_notification_settings_default_setting_title" = "Настройка по умолчанию"; @@ -735,20 +750,20 @@ "screen_room_notification_settings_error_restoring_default" = "Не удалось восстановить режим по умолчанию, попробуйте еще раз."; "screen_room_notification_settings_error_setting_mode" = "Не удалось настроить режим, попробуйте еще раз."; "screen_room_notification_settings_mentions_only_disclaimer" = "Ваш домашний сервер не поддерживает эту опцию в зашифрованных комнатах, вы не будете получать уведомления в этой комнате."; -"screen_room_notification_settings_mode_all_messages" = "Все сообщения"; +"screen_room_notification_settings_mode_all_messages" = "О всех сообщениях"; "screen_room_notification_settings_room_custom_settings_title" = "В этой комнате уведомить меня о"; "screen_room_retry_send_menu_send_again_action" = "Отправить снова"; "screen_room_retry_send_menu_title" = "Не удалось отправить ваше сообщение"; "screen_room_roles_and_permissions_admins" = "Администраторы"; -"screen_room_roles_and_permissions_change_my_role" = "Измените мою роль"; -"screen_room_roles_and_permissions_change_role_demote_to_member" = "Понижение до участника"; +"screen_room_roles_and_permissions_change_my_role" = "Изменить мою роль"; +"screen_room_roles_and_permissions_change_role_demote_to_member" = "Понизить до участника"; "screen_room_roles_and_permissions_change_role_demote_to_moderator" = "Понизить до модератора"; "screen_room_roles_and_permissions_member_moderation" = "Модерация участников"; "screen_room_roles_and_permissions_messages_and_content" = "Сообщения и содержание"; "screen_room_roles_and_permissions_moderators" = "Модераторы"; "screen_room_roles_and_permissions_permissions_header" = "Разрешения"; "screen_room_roles_and_permissions_reset" = "Сбросить разрешения"; -"screen_room_roles_and_permissions_reset_confirm_description" = "Как только вы сбросите разрешения, вы потеряете текущие настройки."; +"screen_room_roles_and_permissions_reset_confirm_description" = "Как только вы сбросите разрешения, все текущие настройки будут утеряны."; "screen_room_roles_and_permissions_reset_confirm_title" = "Сбросить разрешения?"; "screen_room_roles_and_permissions_roles_header" = "Роли"; "screen_room_roles_and_permissions_room_details" = "Информация о комнате"; @@ -774,7 +789,7 @@ "screen_roomlist_filter_favourites" = "Избранное"; "screen_roomlist_filter_favourites_empty_state_subtitle" = "Добавить чат в избранное можно в настройках чата.\nНа данный момент вы можете убрать фильтры, чтобы увидеть другие ваши чаты."; "screen_roomlist_filter_favourites_empty_state_title" = "У вас пока нет избранных чатов"; -"screen_roomlist_filter_invites" = "Приглашает"; +"screen_roomlist_filter_invites" = "Приглашения"; "screen_roomlist_filter_invites_empty_state_title" = "У вас нет отложенных приглашений."; "screen_roomlist_filter_low_priority" = "Низкий приоритет"; "screen_roomlist_filter_mixed_empty_state_subtitle" = "Вы можете убрать фильтры, чтобы увидеть другие ваши чаты."; @@ -783,14 +798,14 @@ "screen_roomlist_filter_rooms" = "Комнаты"; "screen_roomlist_filter_rooms_empty_state_title" = "Вас пока нет ни в одной комнате"; "screen_roomlist_filter_unreads" = "Непрочитанные"; -"screen_roomlist_filter_unreads_empty_state_title" = "Поздравляю!\nУ вас нет непрочитанных сообщений!"; +"screen_roomlist_filter_unreads_empty_state_title" = "Поздравляем!\nУ вас нет непрочитанных сообщений!"; "screen_roomlist_main_space_title" = "Все чаты"; "screen_roomlist_mark_as_read" = "Пометить как прочитанное"; "screen_roomlist_mark_as_unread" = "Пометить как непрочитанное"; "screen_roomlist_room_directory_button_title" = "Просмотреть все комнаты"; "screen_server_confirmation_message_login_element_dot_io" = "Частный сервер для сотрудников Element."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix — это открытая сеть для безопасной децентрализованной связи."; -"screen_server_confirmation_message_register" = "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем."; +"screen_server_confirmation_message_register" = "Здесь будут храниться ваши разговоры — точно так же, как если бы вы использовали почтового провайдера для хранения своих писем."; "screen_server_confirmation_title_login" = "Вы собираетесь войти в %1$@"; "screen_server_confirmation_title_register" = "Вы собираетесь создать учетную запись на %1$@"; "screen_session_verification_cancelled_subtitle" = "Похоже, что-то не так. Время ожидания запроса либо истекло, либо запрос был отклонен."; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Сравните числа"; "screen_session_verification_complete_subtitle" = "Ваш новый сеанс подтвержден. У него есть доступ к вашим зашифрованным сообщениям, и другие пользователи увидят его как доверенное."; "screen_session_verification_enter_recovery_key" = "Введите ключ восстановления"; +"screen_session_verification_failed_subtitle" = "Запрос был отклонен, так как время ожидания запроса истекло, либо произошла ошибка при проверке."; "screen_session_verification_open_existing_session_subtitle" = "Чтобы получить доступ к зашифрованной истории сообщений, докажите, что это вы."; "screen_session_verification_open_existing_session_title" = "Открыть существующий сеанс"; "screen_session_verification_positive_button_canceled" = "Повторить проверку"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Ожидание соответствия"; "screen_session_verification_ready_subtitle" = "Сравните уникальный набор эмодзи."; "screen_session_verification_request_accepted_subtitle" = "Сравните уникальные смайлики, убедившись, что они расположены в том же порядке."; +"screen_session_verification_request_details_timestamp" = "Вход выполнен"; +"screen_session_verification_request_footer" = "Продолжайте только в том случае, если вы инициировали эту проверку."; +"screen_session_verification_request_subtitle" = "Чтобы сохранить историю сообщений в безопасности, проверьте другое устройство."; +"screen_session_verification_request_title" = "Запрос на верификация"; "screen_session_verification_they_dont_match" = "Они не совпадают"; "screen_session_verification_they_match" = "Они совпадают"; "screen_session_verification_waiting_to_accept_subtitle" = "Для продолжения работы примите запрос на запуск процесса проверки в другом сеансе."; @@ -832,7 +852,7 @@ "screen_signout_recovery_disabled_subtitle" = "Вы собираетесь выйти из последнего сеанса. Если вы выйдете из системы сейчас, вы потеряете доступ к зашифрованным сообщениям."; "screen_signout_recovery_disabled_title" = "Восстановление не настроено"; "screen_signout_save_recovery_key_subtitle" = "Вы собираетесь выйти из последнего сеанса. Если вы выйдете из системы сейчас, вы можете потерять доступ к зашифрованным сообщениям."; -"screen_start_chat_error_starting_chat" = "Произошла ошибка при попытке открытия комнаты"; +"screen_start_chat_error_starting_chat" = "Произошла ошибка при запуске чата"; "screen_view_location_title" = "Местоположение"; "screen_welcome_bullet_1" = "Звонки, опросы, поиск и многое другое будут добавлены позже в этом году."; "screen_welcome_bullet_2" = "История сообщений для зашифрованных комнат в этом обновлении будет недоступна."; @@ -846,10 +866,10 @@ "settings_rageshake_detection_threshold" = "Порог обнаружения"; "settings_version_number" = "Версия: %1$@ (%2$@)"; "state_event_avatar_changed_too" = "(изображение тоже было изменено)"; -"state_event_avatar_url_changed" = "%1$@ сменили свое изображение"; +"state_event_avatar_url_changed" = "%1$@ сменил своё изображение"; "state_event_avatar_url_changed_by_you" = "Вы сменили изображение профиля"; -"state_event_demoted_to_member" = "%1$@ был понижен в должности до участника"; -"state_event_demoted_to_moderator" = "%1$@ был понижен в должности до модератора"; +"state_event_demoted_to_member" = "%1$@ был понижен до участника"; +"state_event_demoted_to_moderator" = "%1$@ был понижен до модератора"; "state_event_display_name_changed_from" = "%1$@ изменил свое отображаемое имя с %2$@ на %3$@"; "state_event_display_name_changed_from_by_you" = "Вы изменили свое отображаемое имя с %1$@ на %2$@"; "state_event_display_name_removed" = "%1$@ удалил свое отображаемое имя (оно было %2$@)"; @@ -883,7 +903,7 @@ "state_event_room_knock_retracted" = "%1$@ больше не заинтересован в присоединении"; "state_event_room_knock_retracted_by_you" = "Вы отменили запрос на присоединение"; "state_event_room_leave" = "%1$@ покинул комнату"; -"state_event_room_leave_by_you" = "Вы вышли из комнаты"; +"state_event_room_leave_by_you" = "Вы покинули комнату"; "state_event_room_name_changed" = "%1$@ изменил название комнаты на: %2$@"; "state_event_room_name_changed_by_you" = "Вы изменили название комнаты на: %1$@"; "state_event_room_name_removed" = "%1$@ удалил название комнаты"; @@ -964,11 +984,11 @@ "notification_room_action_mark_as_read" = "Пометить как прочитанное"; "notification_room_action_quick_reply" = "Быстрый ответ"; "screen_pinned_timeline_screen_title_empty" = "Закрепленные сообщения"; -"screen_room_mentions_at_room_title" = "Для всех"; +"screen_room_mentions_at_room_title" = "Все"; "screen_account_provider_change" = "Сменить поставщика учетной записи"; -"screen_account_provider_signin_subtitle" = "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем."; -"screen_account_provider_signup_subtitle" = "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем."; -"screen_analytics_settings_help_us_improve" = "Предоставлять анонимные данные об использовании, чтобы помочь нам выявить проблемы."; +"screen_account_provider_signin_subtitle" = "Здесь будут храниться ваши разговоры — точно так же, как если бы вы использовали почтового провайдера для хранения своих писем."; +"screen_account_provider_signup_subtitle" = "Здесь будут храниться ваши разговоры — точно так же, как если бы вы использовали почтового провайдера для хранения своих писем."; +"screen_analytics_settings_help_us_improve" = "Предоставьте разработчикам анонимные данные об использовании, чтобы помочь им выявлять проблемы эффективнее."; "screen_analytics_settings_read_terms" = "Вы можете ознакомиться со всеми нашими условиями %1$@."; "screen_analytics_settings_read_terms_content_link" = "здесь"; "screen_blocked_users_unblock_alert_action" = "Разблокировать"; @@ -996,7 +1016,7 @@ "screen_report_content_block_user" = "Заблокировать пользователя"; "screen_reset_encryption_password_placeholder" = "Вход…"; "screen_room_attachment_source_camera_photo" = "Сделать фото"; -"screen_room_change_permissions_everyone" = "Для всех"; +"screen_room_change_permissions_everyone" = "Все"; "screen_room_change_permissions_member_moderation" = "Модерация участников"; "screen_room_change_permissions_messages_and_content" = "Сообщения и содержание"; "screen_room_change_permissions_room_details" = "Информация о комнате"; diff --git a/ElementX/Resources/Localizations/ru.lproj/Localizable.stringsdict b/ElementX/Resources/Localizations/ru.lproj/Localizable.stringsdict index 2a5f2f2586..71a17080b1 100644 --- a/ElementX/Resources/Localizations/ru.lproj/Localizable.stringsdict +++ b/ElementX/Resources/Localizations/ru.lproj/Localizable.stringsdict @@ -193,11 +193,11 @@ NSStringFormatValueTypeKey d one - Вы попытались разблокировать %1$d раз + У вас осталась %1$d попытка на разблокировку few - Вы попытались разблокировать %1$d раз + У вас остались %1$d попытки на разблокировку many - Вы попытались разблокировать много раз + У вас осталось %1$d попыток на разблокировку screen_app_lock_subtitle_wrong_pin @@ -211,11 +211,11 @@ NSStringFormatValueTypeKey d one - Неверный PIN-код. У вас остался %1$d шанс + Неверный PIN-код. У вас осталась %1$d попытка few - Неверный PIN-код. У вас остался %1$d шансов + Неверный PIN-код. У вас остались %1$d попытки many - Неверный PIN-код. У вас остался %1$d шанса + Неверный PIN-код. У вас осталось %1$d попыток screen_pinned_timeline_screen_title @@ -229,11 +229,11 @@ NSStringFormatValueTypeKey d one - %1$d Закрепленное сообщение + %1$d закреплённое сообщение few - %1$d Закрепленных сообщений + %1$d закреплённых сообщения many - %1$d Закрепленных сообщений + %1$d закреплённых сообщений screen_room_member_list_header_title diff --git a/ElementX/Resources/Localizations/sk.lproj/Localizable.strings b/ElementX/Resources/Localizations/sk.lproj/Localizable.strings index ca74610ed9..0b62d918e3 100644 --- a/ElementX/Resources/Localizations/sk.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/sk.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Zabudnuté heslo?"; "action_forward" = "Preposlať"; "action_go_back" = "Ísť späť"; +"action_ignore" = "Ignore"; "action_invite" = "Pozvať"; "action_invite_friends" = "Pozvať ľudí"; "action_invite_friends_to_app" = "Pozvať ľudí do %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Tmavý"; "common_decryption_error" = "Chyba dešifrovania"; "common_developer_options" = "Možnosti pre vývojárov"; +"common_device_id" = "Device ID"; "common_direct_chat" = "Priama konverzácia"; "common_edited_suffix" = "(upravené)"; "common_editing" = "Upravuje sa"; @@ -226,6 +228,8 @@ "common_username" = "Používateľské meno"; "common_verification_cancelled" = "Overovanie zrušené"; "common_verification_complete" = "Overovanie je dokončené"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "Overiť zariadenie"; "common_video" = "Video"; "common_voice_message" = "Hlasová správa"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Potvrďte svoj kľúč na obnovenie"; "crash_detection_dialog_content" = "%1$@ zlyhal pri poslednom použití. Chcete zdieľať správu o páde s našim tímom?"; "crypto_identity_change_pin_violation" = "Zdá sa, že totožnosť používateľa %1$@ sa zmenila.%2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Aby aplikácia mohla používať fotoaparát, udeľte povolenie v systémových nastaveniach."; "dialog_permission_generic" = "Udeľte prosím povolenie v systémových nastaveniach."; "dialog_permission_location_description_ios" = "Udeľte prístup v časti Nastavenia -> Poloha."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Stlačte správu a vyberte možnosť „%1$@“, ktorú chcete zahrnúť sem."; "screen_pinned_timeline_empty_state_headline" = "Pripnite dôležité správy, aby sa dali ľahko nájsť"; "screen_reset_encryption_password_error" = "Nastala neznáma chyba. Skontrolujte, či je heslo vášho účtu správne a skúste to znova."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Odblokovať"; "screen_room_member_details_unblock_alert_description" = "Všetky správy od nich budete môcť opäť vidieť."; "screen_room_member_details_unblock_user" = "Odblokovať používateľa"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Zakázať"; "screen_room_member_list_ban_member_confirmation_description" = "Nebudú sa môcť pripojiť k tejto miestnosti znova ani ak budú pozvaní."; "screen_room_member_list_ban_member_confirmation_title" = "Ste si istý, že chcete zakázať tohto člena?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Porovnať čísla"; "screen_session_verification_complete_subtitle" = "Vaša nová relácia je teraz overená. Má prístup k vašim zašifrovaným správam a ostatní používatelia ju budú vidieť ako dôveryhodnú."; "screen_session_verification_enter_recovery_key" = "Zadajte kľúč na obnovenie"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Dokážte, že ste to vy, aby ste získali prístup k histórii vašich zašifrovaných správ."; "screen_session_verification_open_existing_session_title" = "Otvoriť existujúcu reláciu"; "screen_session_verification_positive_button_canceled" = "Zopakovať overenie"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Čaká sa na zhodu"; "screen_session_verification_ready_subtitle" = "Porovnajte jedinečnú sadu emotikonov."; "screen_session_verification_request_accepted_subtitle" = "Porovnajte jedinečné emotikony a uistite sa, že sú zobrazené v rovnakom poradí."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Nezhodujú sa"; "screen_session_verification_they_match" = "Zhodujú sa"; "screen_session_verification_waiting_to_accept_subtitle" = "Ak chcete pokračovať, prijmite žiadosť o spustenie procesu overenia vo vašej druhej relácii."; diff --git a/ElementX/Resources/Localizations/sv.lproj/Localizable.strings b/ElementX/Resources/Localizations/sv.lproj/Localizable.strings index 0609073b10..4486249e57 100644 --- a/ElementX/Resources/Localizations/sv.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/sv.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Glömt lösenordet?"; "action_forward" = "Vidarebefordra"; "action_go_back" = "Gå tillbaka"; +"action_ignore" = "Ignore"; "action_invite" = "Bjud in"; "action_invite_friends" = "Bjud in personer"; "action_invite_friends_to_app" = "Bjud in personer till %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Mörkt"; "common_decryption_error" = "Avkrypteringsfel"; "common_developer_options" = "Utvecklaralternativ"; +"common_device_id" = "Device ID"; "common_direct_chat" = "Direktchatt"; "common_edited_suffix" = "(redigerad)"; "common_editing" = "Redigerar"; @@ -226,6 +228,8 @@ "common_username" = "Användarnamn"; "common_verification_cancelled" = "Verifiering avbruten"; "common_verification_complete" = "Verifieringen slutförd"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "Verifiera enheten"; "common_video" = "Video"; "common_voice_message" = "Röstmeddelande"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Ange din återställningsnyckel"; "crash_detection_dialog_content" = "%1$@ kraschade senast den användes. Vill du dela en kraschrapport med oss?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "För att låta programmet använda kameran, vänligen ge behörigheten i systeminställningarna."; "dialog_permission_generic" = "Vänligen ge behörigheten i systeminställningarna."; "dialog_permission_location_description_ios" = "Ge åtkomst i Inställningar -> Plats."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Tryck på ett meddelande och välj ”%1$@” för att inkludera det här."; "screen_pinned_timeline_empty_state_headline" = "Fäst viktiga meddelanden så att de lätt kan upptäckas"; "screen_reset_encryption_password_error" = "Ett okänt fel inträffade. Kontrollera att ditt kontolösenord är korrekt och försök igen."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Avblockera"; "screen_room_member_details_unblock_alert_description" = "Du kommer att kunna se alla meddelanden från dem igen."; "screen_room_member_details_unblock_user" = "Avblockera användare"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Banna"; "screen_room_member_list_ban_member_confirmation_description" = "Denne kommer inte att kunna gå med i det här rummet igen om denne bjuds in."; "screen_room_member_list_ban_member_confirmation_title" = "Är du säker på att du vill banna den här medlemmen?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Jämför siffror"; "screen_session_verification_complete_subtitle" = "Din nya session är nu verifierad. Den har tillgång till dina krypterade meddelanden, och andra användare kommer att se den som betrodd."; "screen_session_verification_enter_recovery_key" = "Ange återställningsnyckel"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Bevisa att det är du för att komma åt din krypterade meddelandehistorik."; "screen_session_verification_open_existing_session_title" = "Öppna en befintlig session"; "screen_session_verification_positive_button_canceled" = "Försök att verifiera igen"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Väntar på att matcha"; "screen_session_verification_ready_subtitle" = "Jämför en unik uppsättning emojis."; "screen_session_verification_request_accepted_subtitle" = "Jämför de unika emojierna och se till att de visas i samma ordning."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "De matchar inte"; "screen_session_verification_they_match" = "De matchar"; "screen_session_verification_waiting_to_accept_subtitle" = "Godkänn begäran om att starta verifieringsprocessen på din andra session för att fortsätta."; diff --git a/ElementX/Resources/Localizations/uk.lproj/Localizable.strings b/ElementX/Resources/Localizations/uk.lproj/Localizable.strings index 0c8ac30427..0f0b4fbd20 100644 --- a/ElementX/Resources/Localizations/uk.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/uk.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Забули пароль?"; "action_forward" = "Переслати"; "action_go_back" = "Повернутися"; +"action_ignore" = "Ignore"; "action_invite" = "Запросити"; "action_invite_friends" = "Запросити людей"; "action_invite_friends_to_app" = "Запросити людей до %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Темна"; "common_decryption_error" = "Помилка розшифровки"; "common_developer_options" = "Налаштування розробника"; +"common_device_id" = "Device ID"; "common_direct_chat" = "Особистий чат"; "common_edited_suffix" = "(відредаговано)"; "common_editing" = "Редагування"; @@ -226,6 +228,8 @@ "common_username" = "Ім'я користувача"; "common_verification_cancelled" = "Верифікацію скасовано"; "common_verification_complete" = "Верифікацію завершено"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "Перевірте пристрій"; "common_video" = "Відео"; "common_voice_message" = "Голосове повідомлення"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Підтвердіть ключ відновлення"; "crash_detection_dialog_content" = "%1$@ аварійно завершив роботу під час останнього використання. Бажаєте поділитися з нами звітом про збій?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Для того, щоб дозволити програмі використовувати камеру, надайте дозвіл у системних налаштуваннях."; "dialog_permission_generic" = "Будь ласка, надайте дозвіл в системних налаштуваннях."; "dialog_permission_location_description_ios" = "Надайте доступ в Налаштуваннях -> Місцезнаходження."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Розблокувати"; "screen_room_member_details_unblock_alert_description" = "Ви знову зможете бачити всі повідомлення від них."; "screen_room_member_details_unblock_user" = "Розблокувати користувача"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Заблокувати"; "screen_room_member_list_ban_member_confirmation_description" = "Він не зможе приєднатися до цієї кімнати знову, якщо його запросять."; "screen_room_member_list_ban_member_confirmation_title" = "Ви точно хочете заблокувати цього користувача?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Порівняйте цифри"; "screen_session_verification_complete_subtitle" = "Ваш новий сеанс підтверджено. Він матиме доступ до ваших зашифрованих повідомлень, й інші користувачі вважатимуть його надійним."; "screen_session_verification_enter_recovery_key" = "Введіть ключ відновлення"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Доведіть, що це Ви, щоб отримати доступ до історії зашифрованих повідомлень."; "screen_session_verification_open_existing_session_title" = "Відкрийте існуючий сеанс"; "screen_session_verification_positive_button_canceled" = "Повторити перевірку"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Очікування збігу"; "screen_session_verification_ready_subtitle" = "Порівняйте унікальний набір емоджи."; "screen_session_verification_request_accepted_subtitle" = "Порівняйте унікальні емодзі, переконавшись, що вони відображаються в однаковому порядку."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Вони не збігаються"; "screen_session_verification_they_match" = "Вони збігаються"; "screen_session_verification_waiting_to_accept_subtitle" = "Щоб продовжити, прийміть запит на початок процесу перевірки в іншому сеансі."; diff --git a/ElementX/Resources/Localizations/uz.lproj/Localizable.strings b/ElementX/Resources/Localizations/uz.lproj/Localizable.strings index 2d89505b08..517aa829f1 100644 --- a/ElementX/Resources/Localizations/uz.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/uz.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "Parolni unutdingizmi?"; "action_forward" = "Oldinga"; "action_go_back" = "Go back"; +"action_ignore" = "Ignore"; "action_invite" = "Taklif qilish"; "action_invite_friends" = "Odamlarni taklif qiling"; "action_invite_friends_to_app" = "Odamlarni taklif qilish%1$@"; @@ -133,6 +134,7 @@ "common_dark" = "Dark"; "common_decryption_error" = "Shifrni ochish xatosi"; "common_developer_options" = "Dasturchi variantlari"; +"common_device_id" = "Device ID"; "common_direct_chat" = "Direct chat"; "common_edited_suffix" = "(tahrirlangan)"; "common_editing" = "Tahrirlash"; @@ -226,6 +228,8 @@ "common_username" = "Foydalanuvchi nomi"; "common_verification_cancelled" = "Tasdiqlash bekor qilindi"; "common_verification_complete" = "Tasdiqlash yakunlandi"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "Verify device"; "common_video" = "Video"; "common_voice_message" = "Ovozli xabar"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "Enter your recovery key"; "crash_detection_dialog_content" = "%1$@oxirgi marta ishlatilganda qulab tushdi. Biz bilan nosozlik hisobotini baham ko'rmoqchimisiz?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Ilovaga kameradan foydalanishiga ruxsat berish uchun tizim sozlamalarida ruxsat bering."; "dialog_permission_generic" = "Iltimos, tizim sozlamalarida ruxsat bering."; "dialog_permission_location_description_ios" = "Grant access in Settings -> Location."; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -502,7 +515,7 @@ "screen_invites_empty_list" = "Takliflar yo'q"; "screen_invites_invited_you" = "%1$@(%2$@ ) sizni taklif qildi"; "screen_join_room_join_action" = "Join room"; -"screen_join_room_knock_action" = "Knock to join"; +"screen_join_room_knock_action" = "Send request to join"; "screen_join_room_space_not_supported_description" = "%1$@ does not support spaces yet. You can access spaces on web."; "screen_join_room_space_not_supported_title" = "Spaces are not supported yet"; "screen_join_room_subtitle_knock" = "Click the button below and a room administrator will be notified. You’ll be able to join the conversation once approved."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "Blokdan chiqarish"; "screen_room_member_details_unblock_alert_description" = "Ulardan kelgan barcha xabarlarni yana koʻrishingiz mumkin boʻladi."; "screen_room_member_details_unblock_user" = "Foydalanuvchini blokdan chiqarish"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Ban"; "screen_room_member_list_ban_member_confirmation_description" = "They won’t be able to join this room again if invited."; "screen_room_member_list_ban_member_confirmation_title" = "Are you sure you want to ban this member?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Compare numbers"; "screen_session_verification_complete_subtitle" = "Yangi seansingiz tasdiqlandi. U sizning shifrlangan xabarlaringizga kirish huquqiga ega va boshqa foydalanuvchilar uni ishonchli deb bilishadi."; "screen_session_verification_enter_recovery_key" = "Enter recovery key"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "Shifrlangan xabarlar tarixiga kirish uchun shaxsingizni tasdiqlang."; "screen_session_verification_open_existing_session_title" = "Mavjud seansni oching"; "screen_session_verification_positive_button_canceled" = "Tasdiqlashni qaytadan urining"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Mos kelishi kutilmoqda"; "screen_session_verification_ready_subtitle" = "Compare a unique set of emojis."; "screen_session_verification_request_accepted_subtitle" = "Noyob emojilarni solishtiring, ular bir xil tartibda paydo bo'lishiga ishonch hosil qiling."; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Ular mos kelmaydi"; "screen_session_verification_they_match" = "Ular mos keladi"; "screen_session_verification_waiting_to_accept_subtitle" = "Davom etish uchun boshqa seansda tekshirish jarayonini boshlash soʻrovini qabul qiling."; diff --git a/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings b/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings index ee790a99d5..7da78d194c 100644 --- a/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "忘记密码?"; "action_forward" = "转发"; "action_go_back" = "返回"; +"action_ignore" = "Ignore"; "action_invite" = "邀请"; "action_invite_friends" = "邀请朋友"; "action_invite_friends_to_app" = "邀请朋友加入 %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "暗色"; "common_decryption_error" = "解密错误"; "common_developer_options" = "开发者选项"; +"common_device_id" = "Device ID"; "common_direct_chat" = "私聊"; "common_edited_suffix" = "(已编辑)"; "common_editing" = "编辑中"; @@ -226,6 +228,8 @@ "common_username" = "用户名"; "common_verification_cancelled" = "验证已取消"; "common_verification_complete" = "验证完成"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "验证设备"; "common_video" = "视频"; "common_voice_message" = "语音消息"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "输入恢复密钥"; "crash_detection_dialog_content" = "%1$@ 上次使用时崩溃了。想和我们分享崩溃报告吗?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "为了让应用程序使用相机,请在系统设置中授予权限。"; "dialog_permission_generic" = "请在系统设置中授予权限。"; "dialog_permission_location_description_ios" = "在设置->位置中授予访问权限。"; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "按下消息并选择 “%1$@” 将其包含在此处。"; "screen_pinned_timeline_empty_state_headline" = "固定重要消息,以便轻松发现它们"; "screen_reset_encryption_password_error" = "发生未知错误。请检查您的帐户密码是否正确,然后重试。"; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "解封"; "screen_room_member_details_unblock_alert_description" = "可以重新接收他们的消息。"; "screen_room_member_details_unblock_user" = "解封用户"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "封禁"; "screen_room_member_list_ban_member_confirmation_description" = "即使受到邀请,他们也无法再次加入房间。"; "screen_room_member_list_ban_member_confirmation_title" = "您确定要封禁该成员吗?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "比较数字"; "screen_session_verification_complete_subtitle" = "新设备已经成功验证。现在新设备可以访问加密信息,其他用户也会信任这个设备。"; "screen_session_verification_enter_recovery_key" = "输入恢复密钥"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "证明自己的身份以访问加密历史消息。"; "screen_session_verification_open_existing_session_title" = "打开已有会话"; "screen_session_verification_positive_button_canceled" = "重试验证"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "等待比对"; "screen_session_verification_ready_subtitle" = "比较一组表情符号。"; "screen_session_verification_request_accepted_subtitle" = "比较表情符号,确保它们以相同顺序排列。"; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "不匹配"; "screen_session_verification_they_match" = "匹配"; "screen_session_verification_waiting_to_accept_subtitle" = "请在其他会话中接受验证请求。"; diff --git a/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings b/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings index a085b2c6a1..48581fa75b 100644 --- a/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings @@ -54,6 +54,7 @@ "action_forgot_password" = "忘記密碼?"; "action_forward" = "轉寄"; "action_go_back" = "返回"; +"action_ignore" = "Ignore"; "action_invite" = "邀請"; "action_invite_friends" = "邀請夥伴"; "action_invite_friends_to_app" = "邀請朋友使用 %1$@"; @@ -133,6 +134,7 @@ "common_dark" = "深色"; "common_decryption_error" = "解密錯誤"; "common_developer_options" = "開發者選項"; +"common_device_id" = "Device ID"; "common_direct_chat" = "私訊"; "common_edited_suffix" = "(已編輯)"; "common_editing" = "編輯中"; @@ -226,6 +228,8 @@ "common_username" = "使用者名稱"; "common_verification_cancelled" = "驗證已取消"; "common_verification_complete" = "驗證完成"; +"common_verification_failed" = "Verification failed"; +"common_verified" = "Verified"; "common_verify_device" = "驗證裝置"; "common_video" = "影片"; "common_voice_message" = "語音訊息"; @@ -241,6 +245,8 @@ "confirm_recovery_key_banner_title" = "輸入您的復原金鑰"; "crash_detection_dialog_content" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "為了讓應用程式使用相機,請到系統設定中開啟權限。"; "dialog_permission_generic" = "請到系統設定中開啟權限。"; "dialog_permission_location_description_ios" = "在「設定」->「位置」中開啟權限。"; @@ -339,6 +345,13 @@ "screen_create_room_access_section_header" = "Room Access"; "screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; "screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; +"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; +"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; +"screen_join_room_knock_sent_title" = "Request to join sent"; "screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; "screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; "screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; @@ -502,7 +515,7 @@ "screen_invites_empty_list" = "沒有邀請"; "screen_invites_invited_you" = "%1$@(%2$@)邀請您"; "screen_join_room_join_action" = "Join room"; -"screen_join_room_knock_action" = "Knock to join"; +"screen_join_room_knock_action" = "Send request to join"; "screen_join_room_space_not_supported_description" = "%1$@ does not support spaces yet. You can access spaces on web."; "screen_join_room_space_not_supported_title" = "Spaces are not supported yet"; "screen_join_room_subtitle_knock" = "Click the button below and a room administrator will be notified. You’ll be able to join the conversation once approved."; @@ -703,6 +716,8 @@ "screen_room_member_details_unblock_alert_action" = "解除封鎖"; "screen_room_member_details_unblock_alert_description" = "您將無法看到任何來自他們的訊息。"; "screen_room_member_details_unblock_user" = "解除封鎖使用者"; +"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; +"screen_room_member_details_verify_button_title" = "Verify %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "加入黑名單"; "screen_room_member_list_ban_member_confirmation_description" = "即使收到邀請,他們仍然無法加入聊天室。"; "screen_room_member_list_ban_member_confirmation_title" = "您確定要將此成員加入黑名單?"; @@ -800,6 +815,7 @@ "screen_session_verification_compare_numbers_title" = "Compare numbers"; "screen_session_verification_complete_subtitle" = "新的工作階段已完成驗證。它能夠存取您的加密訊息,而其他使用者會將它視為可信任的。"; "screen_session_verification_enter_recovery_key" = "Enter recovery key"; +"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_open_existing_session_subtitle" = "為了存取被加密的歷史訊息,您需要證明這是您本人。"; "screen_session_verification_open_existing_session_title" = "開啟一個現存的工作階段"; "screen_session_verification_positive_button_canceled" = "重新嘗試驗證"; @@ -807,6 +823,10 @@ "screen_session_verification_positive_button_verifying_ongoing" = "等待比對"; "screen_session_verification_ready_subtitle" = "比對一組唯一的表情符號。"; "screen_session_verification_request_accepted_subtitle" = "表情符號是唯一的,請相互比對,確認它們的排列順序是否相同。"; +"screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; +"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "不一樣"; "screen_session_verification_they_match" = "一樣"; "screen_session_verification_waiting_to_accept_subtitle" = "準備開始驗證,請到您的其他工作階段接受請求。"; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 11dfb30e47..0543707297 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -140,6 +140,8 @@ internal enum L10n { internal static var actionForward: String { return L10n.tr("Localizable", "action_forward") } /// Go back internal static var actionGoBack: String { return L10n.tr("Localizable", "action_go_back") } + /// Ignore + internal static var actionIgnore: String { return L10n.tr("Localizable", "action_ignore") } /// Invite internal static var actionInvite: String { return L10n.tr("Localizable", "action_invite") } /// Invite people @@ -298,6 +300,8 @@ internal enum L10n { internal static var commonDecryptionError: String { return L10n.tr("Localizable", "common_decryption_error") } /// Developer options internal static var commonDeveloperOptions: String { return L10n.tr("Localizable", "common_developer_options") } + /// Device ID + internal static var commonDeviceId: String { return L10n.tr("Localizable", "common_device_id") } /// Direct chat internal static var commonDirectChat: String { return L10n.tr("Localizable", "common_direct_chat") } /// (edited) @@ -502,6 +506,8 @@ internal enum L10n { internal static var commonVerificationCancelled: String { return L10n.tr("Localizable", "common_verification_cancelled") } /// Verification complete internal static var commonVerificationComplete: String { return L10n.tr("Localizable", "common_verification_complete") } + /// Verification failed + internal static var commonVerificationFailed: String { return L10n.tr("Localizable", "common_verification_failed") } /// Verified internal static var commonVerified: String { return L10n.tr("Localizable", "common_verified") } /// Verify device @@ -1191,6 +1197,12 @@ internal enum L10n { } /// Cancel request internal static var screenJoinRoomCancelKnockAction: String { return L10n.tr("Localizable", "screen_join_room_cancel_knock_action") } + /// Yes, cancel + internal static var screenJoinRoomCancelKnockAlertConfirmation: String { return L10n.tr("Localizable", "screen_join_room_cancel_knock_alert_confirmation") } + /// Are you sure that you want to cancel your request to join this room? + internal static var screenJoinRoomCancelKnockAlertDescription: String { return L10n.tr("Localizable", "screen_join_room_cancel_knock_alert_description") } + /// Cancel request to join + internal static var screenJoinRoomCancelKnockAlertTitle: String { return L10n.tr("Localizable", "screen_join_room_cancel_knock_alert_title") } /// Join room internal static var screenJoinRoomJoinAction: String { return L10n.tr("Localizable", "screen_join_room_join_action") } /// Send request to join @@ -1723,6 +1735,12 @@ internal enum L10n { internal static var screenRoomMemberDetailsUnblockAlertDescription: String { return L10n.tr("Localizable", "screen_room_member_details_unblock_alert_description") } /// Unblock user internal static var screenRoomMemberDetailsUnblockUser: String { return L10n.tr("Localizable", "screen_room_member_details_unblock_user") } + /// Use the web app to verify this user. + internal static var screenRoomMemberDetailsVerifyButtonSubtitle: String { return L10n.tr("Localizable", "screen_room_member_details_verify_button_subtitle") } + /// Verify %1$@ + internal static func screenRoomMemberDetailsVerifyButtonTitle(_ p1: Any) -> String { + return L10n.tr("Localizable", "screen_room_member_details_verify_button_title", String(describing: p1)) + } /// Ban internal static var screenRoomMemberListBanMemberConfirmationAction: String { return L10n.tr("Localizable", "screen_room_member_list_ban_member_confirmation_action") } /// They won’t be able to join this room again if invited. @@ -1981,6 +1999,8 @@ internal enum L10n { internal static var screenSessionVerificationCompleteSubtitle: String { return L10n.tr("Localizable", "screen_session_verification_complete_subtitle") } /// Enter recovery key internal static var screenSessionVerificationEnterRecoveryKey: String { return L10n.tr("Localizable", "screen_session_verification_enter_recovery_key") } + /// Either the request timed out, the request was denied, or there was a verification mismatch. + internal static var screenSessionVerificationFailedSubtitle: String { return L10n.tr("Localizable", "screen_session_verification_failed_subtitle") } /// Prove it’s you in order to access your encrypted message history. internal static var screenSessionVerificationOpenExistingSessionSubtitle: String { return L10n.tr("Localizable", "screen_session_verification_open_existing_session_subtitle") } /// Open an existing session @@ -1995,6 +2015,14 @@ internal enum L10n { internal static var screenSessionVerificationReadySubtitle: String { return L10n.tr("Localizable", "screen_session_verification_ready_subtitle") } /// Compare the unique emoji, ensuring they appear in the same order. internal static var screenSessionVerificationRequestAcceptedSubtitle: String { return L10n.tr("Localizable", "screen_session_verification_request_accepted_subtitle") } + /// Signed in + internal static var screenSessionVerificationRequestDetailsTimestamp: String { return L10n.tr("Localizable", "screen_session_verification_request_details_timestamp") } + /// Only continue if you initiated this verification. + internal static var screenSessionVerificationRequestFooter: String { return L10n.tr("Localizable", "screen_session_verification_request_footer") } + /// Verify the other device to keep your message history secure. + internal static var screenSessionVerificationRequestSubtitle: String { return L10n.tr("Localizable", "screen_session_verification_request_subtitle") } + /// Verification requested + internal static var screenSessionVerificationRequestTitle: String { return L10n.tr("Localizable", "screen_session_verification_request_title") } /// They don’t match internal static var screenSessionVerificationTheyDontMatch: String { return L10n.tr("Localizable", "screen_session_verification_they_dont_match") } /// They match From 201e3b0c9cd5a3e896122b9e162308ac4200521f Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 21 Oct 2024 15:28:23 +0100 Subject: [PATCH 068/114] Fix an issue calculating the width/height of an image. (#3435) --- ElementX.xcodeproj/project.pbxproj | 60 +++++++++++-------- .../Media/MediaUploadingPreprocessor.swift | 16 ++++- .../Resources/Media/test_rotated_image.jpg | 3 + .../MediaUploadingPreprocessorTests.swift | 40 +++++++++++++ 4 files changed, 93 insertions(+), 26 deletions(-) create mode 100644 UnitTests/Resources/Media/test_rotated_image.jpg diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index dfda797d7f..e68a0d5e07 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 56; + objectVersion = 54; objects = { /* Begin PBXAggregateTarget section */ @@ -225,6 +225,7 @@ 3113065AABBC14CEAE6843FA /* UserSessionFlowCoordinatorStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8774CF614849664B5B3C2A1 /* UserSessionFlowCoordinatorStateMachine.swift */; }; 3116693C5EB476E028990416 /* XCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74611A4182DCF5F4D42696EC /* XCTestCase.swift */; }; 3118D9ABFD4BE5A3492FF88A /* ElementCallConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC437C491EA6996513B1CEAB /* ElementCallConfiguration.swift */; }; + 31A27DD23C8637A0EBA76AFB /* test_rotated_image.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 7229371D48BE92239D852C1B /* test_rotated_image.jpg */; }; 32B7891D937377A59606EDFC /* UserFlowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21DD8599815136EFF5B73F38 /* UserFlowTests.swift */; }; 339BC18777912E1989F2F17D /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = 584A61D9C459FAFEF038A7C0 /* Section.swift */; }; 33CAC1226DFB8B5D8447D286 /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 1BCD21310B997A6837B854D6 /* GZIP */; }; @@ -456,6 +457,7 @@ 661EF50C1F7D4B0BC8A7AAE3 /* EmoteRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44ABA63DBE7F76C58260B43B /* EmoteRoomTimelineView.swift */; }; 66357ECB73B1290E5490A012 /* WebRegistrationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F418426410F3823F3EB0828 /* WebRegistrationScreenViewModelProtocol.swift */; }; 663E198678778F7426A9B27D /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FAFE1C2149E6AC8156ED2B /* Collection.swift */; }; + 6681D6D3ADF69EBD2625F29A /* KnockedRoomProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E8F4D7D61B80EBD5CB92F8A /* KnockedRoomProxyMock.swift */; }; 67160204A8D362BB7D4AD259 /* Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693E16574C6F7F9FA1015A8C /* Search.swift */; }; 6786C4B0936AC84D993B20BF /* NotificationSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F06F2F09B2EDD067DC2174 /* NotificationSettingsScreen.swift */; }; 6793E75E3EBE48EBB8F857AF /* ProcessInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077B01C13BBA2996272C5FB5 /* ProcessInfo.swift */; }; @@ -767,8 +769,6 @@ A6D4C5EEA85A6A0ABA1559D6 /* RoomDetailsEditScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16D09C79746BDCD9173EB3A7 /* RoomDetailsEditScreenModels.swift */; }; A6DEC1ADEC8FEEC206A0FA37 /* AttributedStringBuilderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72F37B5DA798C9AE436F2C2C /* AttributedStringBuilderProtocol.swift */; }; A6F345328CCC5C9B0DAE2257 /* LogViewerScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BB05221D7D941CC82DC8480 /* LogViewerScreenViewModel.swift */; }; - A71F957D2CBFF33100FDBDF2 /* KnockedRoomProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71F957C2CBFF32A00FDBDF2 /* KnockedRoomProxy.swift */; }; - A71F957F2CBFFD2500FDBDF2 /* KnockedRoomProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71F957E2CBFFD1F00FDBDF2 /* KnockedRoomProxyMock.swift */; }; A722F426FD81FC67706BB1E0 /* CustomLayoutLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42236480CF0431535EBE8387 /* CustomLayoutLabelStyle.swift */; }; A74438ED16F8683A4B793E6A /* AnalyticsSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BCE3FAF40932AC7C7639AC4 /* AnalyticsSettingsScreenViewModel.swift */; }; A7D48E44D485B143AADDB77D /* Strings+Untranslated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */; }; @@ -906,6 +906,7 @@ C8C7AF33AADF88B306CD2695 /* QRCodeLoginService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4427AF4B7FB7EF3E3D424C7 /* QRCodeLoginService.swift */; }; C8E0FA0FF2CD6613264FA6B9 /* MessageForwardingScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFEA446F8618DBA79A9239CC /* MessageForwardingScreen.swift */; }; C915347779B3C7FDD073A87A /* AVMetadataMachineReadableCodeObjectExtensionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93E1FF0DFBB3768F79FDBF6D /* AVMetadataMachineReadableCodeObjectExtensionsTest.swift */; }; + C969A62F3D9F14318481A33B /* KnockedRoomProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858DA81F2ACF484B7CAD6AE4 /* KnockedRoomProxy.swift */; }; C97325EFDCCEE457432A9E82 /* MessageText.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1E0B4A34E69BD2132BEC521 /* MessageText.swift */; }; C9A631FD968249B4BA0B7B3C /* ReactionsSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02EE0FABA8ED6D6C1D6CE71D /* ReactionsSummaryView.swift */; }; C9ABF75A43F2D26F1D9A1F27 /* DeactivateAccountScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC3FDB58F57386741A4FC7F /* DeactivateAccountScreenViewModel.swift */; }; @@ -1215,13 +1216,13 @@ 033DB41C51865A2E83174E87 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = ""; }; 035177BCD8E8308B098AC3C2 /* WindowManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowManager.swift; sourceTree = ""; }; 0376C429FAB1687C3D905F3E /* MockCoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCoder.swift; sourceTree = ""; }; - 0392E3FDE372C9B56FEEED8B /* test_voice_message.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_voice_message.m4a; sourceTree = ""; }; + 0392E3FDE372C9B56FEEED8B /* test_voice_message.m4a */ = {isa = PBXFileReference; path = test_voice_message.m4a; sourceTree = ""; }; 03DD998E523D4EC93C7ED703 /* RoomNotificationSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenViewModelProtocol.swift; sourceTree = ""; }; 03FABD73FD8086EFAB699F42 /* MediaUploadPreviewScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModelTests.swift; sourceTree = ""; }; 044E501B8331B339874D1B96 /* CompoundIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompoundIcon.swift; sourceTree = ""; }; 045253F9967A535EE5B16691 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; 046C0D3F53B0B5EF0A1F5BEA /* RoomSummaryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryTests.swift; sourceTree = ""; }; - 048A21188AB19349D026BECD /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; + 048A21188AB19349D026BECD /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 04BB8DDE245ED86C489BA983 /* AccessibilityIdentifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityIdentifiers.swift; sourceTree = ""; }; 04DF593C3F7AF4B2FBAEB05D /* FileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManager.swift; sourceTree = ""; }; 0516C69708D5CBDE1A8E77EC /* RoomDirectorySearchProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchProxyProtocol.swift; sourceTree = ""; }; @@ -1287,7 +1288,7 @@ 127A57D053CE8C87B5EFB089 /* Consumable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Consumable.swift; sourceTree = ""; }; 127C8472672A5BA09EF1ACF8 /* CurrentValuePublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentValuePublisher.swift; sourceTree = ""; }; 128501375217576AF0FE3E92 /* RoomAttachmentPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomAttachmentPicker.swift; sourceTree = ""; }; - 1304D9191300873EADA52D6E /* IntegrationTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = IntegrationTests.xctestplan; sourceTree = ""; }; + 1304D9191300873EADA52D6E /* IntegrationTests.xctestplan */ = {isa = PBXFileReference; path = IntegrationTests.xctestplan; sourceTree = ""; }; 130ED565A078F7E0B59D9D25 /* UNTextInputNotificationResponse+Creator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UNTextInputNotificationResponse+Creator.swift"; sourceTree = ""; }; 136F80A613B55BDD071DCEA5 /* JoinRoomScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenModels.swift; sourceTree = ""; }; 13802897C7AFA360EA74C0B0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = ""; }; @@ -1376,7 +1377,7 @@ 25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationItemProxy.swift; sourceTree = ""; }; 25F8664F1FB95AF3C4202478 /* PollFormScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenCoordinator.swift; sourceTree = ""; }; 260004737C573A56FA01E86E /* Encodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encodable.swift; sourceTree = ""; }; - 267BB1D5B08A9511F894CB57 /* PreviewTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = PreviewTests.xctestplan; sourceTree = ""; }; + 267BB1D5B08A9511F894CB57 /* PreviewTests.xctestplan */ = {isa = PBXFileReference; path = PreviewTests.xctestplan; sourceTree = ""; }; 26B0A96B8FE4849227945067 /* VoiceMessageRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRecorder.swift; sourceTree = ""; }; 26EAAB54C6CE91D64B69A9F8 /* AppLockServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockServiceProtocol.swift; sourceTree = ""; }; 2721D7B051F0159AA919DA05 /* RoomChangePermissionsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangePermissionsScreenViewModelProtocol.swift; sourceTree = ""; }; @@ -1447,7 +1448,7 @@ 3558A15CFB934F9229301527 /* RestorationToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestorationToken.swift; sourceTree = ""; }; 35AFCF4C05DEED04E3DB1A16 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; 35FA991289149D31F4286747 /* UserPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreference.swift; sourceTree = ""; }; - 36DA824791172B9821EACBED /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; + 36DA824791172B9821EACBED /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 36FD673E24FBFCFDF398716A /* RoomMemberProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberProxyMock.swift; sourceTree = ""; }; 376D941BF8BB294389C0DE24 /* MapTilerURLBuildersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerURLBuildersTests.swift; sourceTree = ""; }; 37A243E04B58DC6E41FDCD82 /* EmojiItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiItem.swift; sourceTree = ""; }; @@ -1551,7 +1552,7 @@ 4B41FABA2B0AEF4389986495 /* LoginMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginMode.swift; sourceTree = ""; }; 4BD371B60E07A5324B9507EF /* AnalyticsSettingsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenCoordinator.swift; sourceTree = ""; }; 4C8D988E82A8DFA13BE46F7C /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = pl; path = pl.lproj/Localizable.stringsdict; sourceTree = ""; }; - 4CD6AC7546E8D7E5C73CEA48 /* ElementX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ElementX.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CD6AC7546E8D7E5C73CEA48 /* ElementX.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = ElementX.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4CDDDDD9FE1A699D23A5E096 /* LoginScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreen.swift; sourceTree = ""; }; 4D3A7375AB22721C436EB056 /* ComposerToolbarModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerToolbarModels.swift; sourceTree = ""; }; 4E2245243369B99216C7D84E /* ImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; sourceTree = ""; }; @@ -1699,6 +1700,7 @@ 71BC7CA1BC1041E93077BBA1 /* HomeScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenModels.swift; sourceTree = ""; }; 71D52BAA5BADB06E5E8C295D /* Assets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Assets.swift; sourceTree = ""; }; 71E2E5103702D13361D09100 /* UserProfileScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileScreenViewModelTests.swift; sourceTree = ""; }; + 7229371D48BE92239D852C1B /* test_rotated_image.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test_rotated_image.jpg; sourceTree = ""; }; 72614BFF35B8394C6E13F55A /* TimelineItemStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemStatusView.swift; sourceTree = ""; }; 72F37B5DA798C9AE436F2C2C /* AttributedStringBuilderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringBuilderProtocol.swift; sourceTree = ""; }; 7310D8DFE01AF45F0689C3AA /* Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Publisher.swift; sourceTree = ""; }; @@ -1784,6 +1786,7 @@ 854BCEAF2A832176FAACD2CB /* SplashScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashScreenCoordinator.swift; sourceTree = ""; }; 85666E40F7E817809B4FD787 /* ComposerToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerToolbar.swift; sourceTree = ""; }; 8585C636A10B8141A7AE909F /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/InfoPlist.strings; sourceTree = ""; }; + 858DA81F2ACF484B7CAD6AE4 /* KnockedRoomProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnockedRoomProxy.swift; sourceTree = ""; }; 85EB16E7FE59A947CA441531 /* MediaProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProviderProtocol.swift; sourceTree = ""; }; 8609BE4CA71C30D1FCE3AF9B /* AuthenticationStartScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationStartScreenModels.swift; sourceTree = ""; }; 8610C1D21565C950BCA6A454 /* AppLockSetupSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupSettingsScreenViewModelProtocol.swift; sourceTree = ""; }; @@ -1817,7 +1820,7 @@ 8D55702474F279D910D2D162 /* RoomStateEventStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomStateEventStringBuilder.swift; sourceTree = ""; }; 8D8169443E5AC5FF71BFB3DB /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; 8DA1E8F287680C8ED25EDBAC /* NetworkMonitorMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitorMock.swift; sourceTree = ""; }; - 8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UITests.xctestplan; sourceTree = ""; }; + 8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; path = UITests.xctestplan; sourceTree = ""; }; 8E1584F8BCF407BB94F48F04 /* EncryptionResetPasswordScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetPasswordScreen.swift; sourceTree = ""; }; 8EAF4A49F3ACD8BB8B0D2371 /* ClientSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientSDKMock.swift; sourceTree = ""; }; 8F21ED7205048668BEB44A38 /* AppActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppActivityView.swift; sourceTree = ""; }; @@ -1887,6 +1890,7 @@ 9CE3C90E487B255B735D73C8 /* RoomScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenViewModel.swift; sourceTree = ""; }; 9CF1EE0AA78470C674554262 /* PillTextAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillTextAttachment.swift; sourceTree = ""; }; 9E6D88E8AFFBF2C1D589C0FA /* UIConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIConstants.swift; sourceTree = ""; }; + 9E8F4D7D61B80EBD5CB92F8A /* KnockedRoomProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnockedRoomProxyMock.swift; sourceTree = ""; }; 9EB9BA2F30EB8C33226D8FF1 /* UserSessionStoreMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionStoreMock.swift; sourceTree = ""; }; 9ECF11669EF253E98AA2977A /* CompletionSuggestionServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletionSuggestionServiceProtocol.swift; sourceTree = ""; }; 9F1DF3FFFE5ED2B8133F43A7 /* MessageComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposer.swift; sourceTree = ""; }; @@ -1919,8 +1923,6 @@ A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHGPostHogConfiguration.swift; sourceTree = ""; }; A6C11AD9813045E44F950410 /* ElementCallWidgetDriverProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallWidgetDriverProtocol.swift; sourceTree = ""; }; A6EA0D8B0BBD8805F7D5A133 /* TextBasedRoomTimelineViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextBasedRoomTimelineViewProtocol.swift; sourceTree = ""; }; - A71F957C2CBFF32A00FDBDF2 /* KnockedRoomProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnockedRoomProxy.swift; sourceTree = ""; }; - A71F957E2CBFFD1F00FDBDF2 /* KnockedRoomProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnockedRoomProxyMock.swift; sourceTree = ""; }; A73A07BAEDD74C48795A996A /* AsyncSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncSequence.swift; sourceTree = ""; }; A7978C9EFBDD7DE39BD86726 /* RestorationTokenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestorationTokenTests.swift; sourceTree = ""; }; A7C4EA55DA62F9D0F984A2AE /* CollapsibleTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleTimelineItem.swift; sourceTree = ""; }; @@ -1995,7 +1997,7 @@ B50F03079F6B5EF9CA005F14 /* TimelineProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineProxyProtocol.swift; sourceTree = ""; }; B53AC78E49A297AC1D72A7CF /* AppMediator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediator.swift; sourceTree = ""; }; B590BD4507D4F0A377FDE01A /* LoadableAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadableAvatarImage.swift; sourceTree = ""; }; - B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = ConfettiScene.scn; sourceTree = ""; }; + B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; path = ConfettiScene.scn; sourceTree = ""; }; B63B69F9A2BC74DD40DC75C8 /* AdvancedSettingsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenViewModel.swift; sourceTree = ""; }; B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetails.swift; sourceTree = ""; }; B655A536341D2695158C6664 /* AuthenticationClientBuilderFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationClientBuilderFactory.swift; sourceTree = ""; }; @@ -2110,7 +2112,7 @@ CDB3227C7A74B734924942E9 /* RoomSummaryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryProvider.swift; sourceTree = ""; }; CDE3F3911FF7CC639BDE5844 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; CEE20623EB4A9B88FB29F2BA /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/SAS.strings; sourceTree = ""; }; - CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UnitTests.xctestplan; sourceTree = ""; }; + CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; path = UnitTests.xctestplan; sourceTree = ""; }; D071F86CD47582B9196C9D16 /* UserDiscoverySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDiscoverySection.swift; sourceTree = ""; }; D086854995173E897F993C26 /* AdvancedSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenViewModelProtocol.swift; sourceTree = ""; }; D09A267106B9585D3D0CFC0D /* ClientError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientError.swift; sourceTree = ""; }; @@ -2241,7 +2243,7 @@ ED0CBEAB5F796BEFBAF7BB6A /* VideoRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineView.swift; sourceTree = ""; }; ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemProtocol.swift; sourceTree = ""; }; ED33988DA4FD4FC666800106 /* SessionVerificationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreenViewModel.swift; sourceTree = ""; }; - ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = message.caf; sourceTree = ""; }; + ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; path = message.caf; sourceTree = ""; }; ED49073BB1C1FC649DAC2CCD /* LocationRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationRoomTimelineView.swift; sourceTree = ""; }; ED60E4D2CD678E1EBF16F77A /* BlockedUsersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedUsersScreen.swift; sourceTree = ""; }; EE378083653EF0C9B5E9D580 /* EmoteRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmoteRoomTimelineItemContent.swift; sourceTree = ""; }; @@ -2263,7 +2265,7 @@ F174A5627CDB3CAF280D1880 /* EmojiPickerScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerScreenModels.swift; sourceTree = ""; }; F17EFA1D3D09FC2F9C5E1CB2 /* MediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProvider.swift; sourceTree = ""; }; F1B8500C152BC59445647DA8 /* UnsupportedRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsupportedRoomTimelineItem.swift; sourceTree = ""; }; - F2D513D2477B57F90E98EEC0 /* portrait_test_video.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = portrait_test_video.mp4; sourceTree = ""; }; + F2D513D2477B57F90E98EEC0 /* portrait_test_video.mp4 */ = {isa = PBXFileReference; path = portrait_test_video.mp4; sourceTree = ""; }; F2E4EF80DFB8FE7C4469B15D /* RoomDirectorySearchScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreen.swift; sourceTree = ""; }; F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixEntityRegexTests.swift; sourceTree = ""; }; F348B5F2C12F9D4F4B4D3884 /* VideoRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineItem.swift; sourceTree = ""; }; @@ -2903,7 +2905,6 @@ 31CE4DA53232AA534057F912 /* Mocks */ = { isa = PBXGroup; children = ( - A71F957E2CBFFD1F00FDBDF2 /* KnockedRoomProxyMock.swift */, 69CB8242D69B7E4D0B32E18D /* AggregatedReactionMock.swift */, 3BAC027034248429A438886B /* AppMediatorMock.swift */, 0554FEA301486A8CFA475D5A /* AuthenticationClientBuilderFactoryMock.swift */, @@ -2913,6 +2914,7 @@ E321E840DCC63790049984F4 /* ElementCallServiceMock.swift */, 1B10423B9102086A2D9BFCBA /* EventTimelineItem.swift */, 867DC9530C42F7B5176BE465 /* JoinedRoomProxyMock.swift */, + 9E8F4D7D61B80EBD5CB92F8A /* KnockedRoomProxyMock.swift */, 6F65E4BB9E82EB8373207CF8 /* MediaProviderMock.swift */, 8DA1E8F287680C8ED25EDBAC /* NetworkMonitorMock.swift */, 382B50F7E379B3DBBD174364 /* NotificationSettingsProxyMock.swift */, @@ -3200,9 +3202,9 @@ 40E6246F03D1FE377BC5D963 /* Room */ = { isa = PBXGroup; children = ( - A71F957C2CBFF32A00FDBDF2 /* KnockedRoomProxy.swift */, 0E95B3BDB80531C85CD50AE6 /* InvitedRoomProxy.swift */, 07C6B0B087FE6601C3F77816 /* JoinedRoomProxy.swift */, + 858DA81F2ACF484B7CAD6AE4 /* KnockedRoomProxy.swift */, B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */, 974AEAF3FE0C577A6C04AD6E /* RoomPermissions.swift */, 47111410B6E659A697D472B5 /* RoomProxyProtocol.swift */, @@ -4714,6 +4716,7 @@ D5E26C54362206BBDD096D83 /* test_audio.mp3 */, C733D11B421CFE3A657EF230 /* test_image.png */, 3FFDA99C98BE05F43A92343B /* test_pdf.pdf */, + 7229371D48BE92239D852C1B /* test_rotated_image.jpg */, 0392E3FDE372C9B56FEEED8B /* test_voice_message.m4a */, ); path = Media; @@ -5891,6 +5894,7 @@ 87CEDB8A0696F0D5AE2ABB28 /* test_audio.mp3 in Resources */, 21BF2B7CEDFE3CA67C5355AD /* test_image.png in Resources */, E77469C5CD7F7F58C0AC9752 /* test_pdf.pdf in Resources */, + 31A27DD23C8637A0EBA76AFB /* test_rotated_image.jpg in Resources */, CBB4F39A1309F7281AE7AA8E /* test_voice_message.m4a in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -6456,7 +6460,6 @@ 46A6DB0F78FB399BD59E2D41 /* EncryptionKeyProviderProtocol.swift in Sources */, 0C6DF318E9C8F6461E6ABDE7 /* EncryptionResetPasswordScreen.swift in Sources */, 36926D795D6D19177C7812F8 /* EncryptionResetPasswordScreenCoordinator.swift in Sources */, - A71F957D2CBFF33100FDBDF2 /* KnockedRoomProxy.swift in Sources */, B1B255CE0E4306DD6E09D936 /* EncryptionResetPasswordScreenModels.swift in Sources */, D6152E21036B88C44ECB22E7 /* EncryptionResetPasswordScreenViewModel.swift in Sources */, A0601810597769B81C2358AF /* EncryptionResetPasswordScreenViewModelProtocol.swift in Sources */, @@ -6537,6 +6540,8 @@ 1FE593ECEC40A43789105D80 /* KeychainController.swift in Sources */, FD29471C72872F8B7580E3E1 /* KeychainControllerMock.swift in Sources */, CB99B0FA38A4AC596F38CC13 /* KeychainControllerProtocol.swift in Sources */, + C969A62F3D9F14318481A33B /* KnockedRoomProxy.swift in Sources */, + 6681D6D3ADF69EBD2625F29A /* KnockedRoomProxyMock.swift in Sources */, 454F8DDC4442C0DE54094902 /* LABiometryType.swift in Sources */, E468CC731C3F4D678499E52F /* LAContextMock.swift in Sources */, D5681C80D8281560AACE0035 /* Label.swift in Sources */, @@ -6614,7 +6619,6 @@ EA01A06EEDFEF4AE7652E5F3 /* NSRegularExpresion.swift in Sources */, FA2BBAE9FC5E2E9F960C0980 /* NavigationCoordinators.swift in Sources */, 71C1347F23868324A4F43940 /* NavigationModule.swift in Sources */, - A71F957F2CBFFD2500FDBDF2 /* KnockedRoomProxyMock.swift in Sources */, B5E455C9689EA600EDB3E9E0 /* NavigationRootCoordinator.swift in Sources */, 93BAF04D9CCBC0A8841414D0 /* NetworkMonitor.swift in Sources */, 96B3606E30F824095B1DD022 /* NetworkMonitorMock.swift in Sources */, @@ -7285,7 +7289,9 @@ "@executable_path/../../Frameworks", ); MARKETING_VERSION = "$(MARKETING_VERSION)"; - OTHER_SWIFT_FLAGS = "-DIS_NSE"; + OTHER_SWIFT_FLAGS = ( + "-DIS_NSE", + ); PRODUCT_BUNDLE_IDENTIFIER = "${BASE_BUNDLE_IDENTIFIER}.nse"; PRODUCT_DISPLAY_NAME = "$(APP_DISPLAY_NAME)"; PRODUCT_NAME = NSE; @@ -7334,7 +7340,9 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = "$(MARKETING_VERSION)"; - OTHER_SWIFT_FLAGS = "-DIS_MAIN_APP"; + OTHER_SWIFT_FLAGS = ( + "-DIS_MAIN_APP", + ); PILLS_UT_TYPE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).pills"; PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(APP_NAME)"; @@ -7360,7 +7368,9 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = "$(MARKETING_VERSION)"; - OTHER_SWIFT_FLAGS = "-DIS_MAIN_APP"; + OTHER_SWIFT_FLAGS = ( + "-DIS_MAIN_APP", + ); PILLS_UT_TYPE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).pills"; PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(APP_NAME)"; @@ -7605,7 +7615,9 @@ "@executable_path/../../Frameworks", ); MARKETING_VERSION = "$(MARKETING_VERSION)"; - OTHER_SWIFT_FLAGS = "-DIS_NSE"; + OTHER_SWIFT_FLAGS = ( + "-DIS_NSE", + ); PRODUCT_BUNDLE_IDENTIFIER = "${BASE_BUNDLE_IDENTIFIER}.nse"; PRODUCT_DISPLAY_NAME = "$(APP_DISPLAY_NAME)"; PRODUCT_NAME = NSE; diff --git a/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift b/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift index 0f967d8a5d..9a9b0bcefc 100644 --- a/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift +++ b/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift @@ -449,10 +449,22 @@ struct MediaUploadingPreprocessor { private extension CGImageSource { var size: CGSize? { guard let properties = CGImageSourceCopyPropertiesAtIndex(self, 0, nil) as? [NSString: Any], - let width = properties[kCGImagePropertyPixelWidth] as? Int, - let height = properties[kCGImagePropertyPixelHeight] as? Int else { + var width = properties[kCGImagePropertyPixelWidth] as? Int, + var height = properties[kCGImagePropertyPixelHeight] as? Int else { return nil } + + // Make sure the width and height are the correct way around if an orientation is set. + if let orientationValue = properties[kCGImagePropertyOrientation] as? UInt32, + let orientation = CGImagePropertyOrientation(rawValue: orientationValue) { + switch orientation { + case .up, .down, .upMirrored, .downMirrored: + break + case .left, .right, .leftMirrored, .rightMirrored: + swap(&width, &height) + } + } + return CGSize(width: width, height: height) } } diff --git a/UnitTests/Resources/Media/test_rotated_image.jpg b/UnitTests/Resources/Media/test_rotated_image.jpg new file mode 100644 index 0000000000..6169c3717d --- /dev/null +++ b/UnitTests/Resources/Media/test_rotated_image.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:094ceac734f4461ae9f1c47eae58c13270da6f407718b12e3142cac3dc0b38a7 +size 1735894 diff --git a/UnitTests/Sources/MediaUploadingPreprocessorTests.swift b/UnitTests/Sources/MediaUploadingPreprocessorTests.swift index 7438c3a84c..a9aa74bd59 100644 --- a/UnitTests/Sources/MediaUploadingPreprocessorTests.swift +++ b/UnitTests/Sources/MediaUploadingPreprocessorTests.swift @@ -422,6 +422,46 @@ final class MediaUploadingPreprocessorTests: XCTestCase { XCTAssertEqual(optimizedImageInfo.height, 498) } + func testRotatedImageProcessing() async { + guard let url = Bundle(for: Self.self).url(forResource: "test_rotated_image.jpg", withExtension: nil) else { + XCTFail("Failed retrieving test asset") + return + } + + guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url), + case let .image(convertedImageURL, thumbnailURL, imageInfo) = result else { + XCTFail("Failed processing asset") + return + } + + compare(originalImageAt: url, toConvertedImageAt: convertedImageURL, withThumbnailAt: thumbnailURL) + + // Check resulting image info + XCTAssertEqual(imageInfo.mimetype, "image/jpeg") + XCTAssertEqual(imageInfo.width, 2848) + XCTAssertEqual(imageInfo.height, 4272) + + XCTAssertNotNil(imageInfo.thumbnailInfo) + XCTAssertEqual(imageInfo.thumbnailInfo?.width, 533) + XCTAssertEqual(imageInfo.thumbnailInfo?.height, 800) + + // Repeat with optimised media setting + appSettings.optimizeMediaUploads = true + + guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url), + case let .image(optimizedImageURL, thumbnailURL, optimizedImageInfo) = optimizedResult else { + XCTFail("Failed processing asset") + return + } + + compare(originalImageAt: url, toConvertedImageAt: optimizedImageURL, withThumbnailAt: thumbnailURL) + + // Check optimised image info + XCTAssertEqual(optimizedImageInfo.mimetype, "image/jpeg") + XCTAssertEqual(optimizedImageInfo.width, 1365) + XCTAssertEqual(optimizedImageInfo.height, 2048) + } + // MARK: - Private private func compare(originalImageAt originalImageURL: URL, toConvertedImageAt convertedImageURL: URL, withThumbnailAt thumbnailURL: URL) { From a0c81cf3934758f20d364c3071de601a3f052e31 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 21 Oct 2024 15:29:03 +0100 Subject: [PATCH 069/114] Add support for rendering media captions in the timeline. (#3429) --- .../Style/TimelineItemBubbledStylerView.swift | 5 +- .../Style/TimelineItemSendInfoLabel.swift | 6 +-- .../ImageRoomTimelineView.swift | 52 +++++++++++++++---- .../VideoRoomTimelineView.swift | 40 ++++++++++++-- ...ventBasedMessageTimelineItemProtocol.swift | 17 ++++++ ...est_imageRoomTimelineView-iPad-en-GB.1.png | 4 +- ...st_imageRoomTimelineView-iPad-pseudo.1.png | 4 +- ...mageRoomTimelineView-iPhone-16-en-GB.1.png | 4 +- ...ageRoomTimelineView-iPhone-16-pseudo.1.png | 4 +- ...est_videoRoomTimelineView-iPad-en-GB.1.png | 4 +- ...st_videoRoomTimelineView-iPad-pseudo.1.png | 4 +- ...ideoRoomTimelineView-iPhone-16-en-GB.1.png | 4 +- ...deoRoomTimelineView-iPhone-16-pseudo.1.png | 4 +- 13 files changed, 116 insertions(+), 36 deletions(-) diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift index 63e2304ba2..0d3fc5360e 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift @@ -257,7 +257,7 @@ private extension EventBasedTimelineItemProtocol { switch self { case is ImageRoomTimelineItem, is VideoRoomTimelineItem: // In case a reply detail or a thread decorator is present we render the color and the padding - return self.replyDetails != nil || self.isThreaded ? defaultColor : nil + return self.replyDetails != nil || self.isThreaded || self.hasMediaCaption ? defaultColor : nil default: return defaultColor } @@ -283,8 +283,7 @@ private extension EventBasedTimelineItemProtocol { // In case a reply detail or a thread decorator is present we render the color and the padding case is ImageRoomTimelineItem, is VideoRoomTimelineItem: - return self.replyDetails != nil || - self.isThreaded ? defaultInsets : .zero + return self.replyDetails != nil || self.isThreaded || self.hasMediaCaption ? defaultInsets : .zero case let locationTimelineItem as LocationRoomTimelineItem: return locationTimelineItem.content.geoURI == nil || self.replyDetails != nil || diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemSendInfoLabel.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemSendInfoLabel.swift index d2cbd56391..b350a9b2e7 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemSendInfoLabel.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemSendInfoLabel.swift @@ -152,9 +152,9 @@ private extension TimelineItemSendInfo { layoutType = switch timelineItem { case is TextBasedRoomTimelineItem: .overlay(capsuleStyle: false) - case is ImageRoomTimelineItem, - is VideoRoomTimelineItem, - is StickerRoomTimelineItem: + case let message as EventBasedMessageTimelineItemProtocol where message is ImageRoomTimelineItem || message is VideoRoomTimelineItem: + .overlay(capsuleStyle: !message.hasMediaCaption) + case is StickerRoomTimelineItem: .overlay(capsuleStyle: true) case let locationTimelineItem as LocationRoomTimelineItem: .overlay(capsuleStyle: locationTimelineItem.content.geoURI != nil) diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift index e86d30f13f..63cb9b04ef 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift @@ -12,18 +12,35 @@ struct ImageRoomTimelineView: View { @EnvironmentObject private var context: TimelineViewModel.Context let timelineItem: ImageRoomTimelineItem + var hasMediaCaption: Bool { timelineItem.content.caption != nil } + var body: some View { TimelineStyler(timelineItem: timelineItem) { - LoadableImage(mediaSource: source, - mediaType: .timelineItem, - blurhash: timelineItem.content.blurhash, - mediaProvider: context.mediaProvider) { - placeholder + VStack(alignment: .leading, spacing: 4) { + LoadableImage(mediaSource: source, + mediaType: .timelineItem, + blurhash: timelineItem.content.blurhash, + mediaProvider: context.mediaProvider) { + placeholder + } + .timelineMediaFrame(height: timelineItem.content.height, + aspectRatio: timelineItem.content.aspectRatio) + .accessibilityElement(children: .ignore) + .accessibilityLabel(L10n.commonImage) + // This clip shape is distinct from the one in the styler as that one + // operates on the entire message so wouldn't round the bottom corners. + .clipShape(RoundedRectangle(cornerRadius: hasMediaCaption ? 6 : 0)) + + if let attributedCaption = timelineItem.content.formattedCaption { + FormattedBodyText(attributedString: attributedCaption, + additionalWhitespacesCount: timelineItem.additionalWhitespaces(), + boostEmojiSize: true) + } else if let caption = timelineItem.content.caption { + FormattedBodyText(text: caption, + additionalWhitespacesCount: timelineItem.additionalWhitespaces(), + boostEmojiSize: true) + } } - .timelineMediaFrame(height: timelineItem.content.height, - aspectRatio: timelineItem.content.aspectRatio) - .accessibilityElement(children: .ignore) - .accessibilityLabel(L10n.commonImage) } } @@ -87,6 +104,23 @@ struct ImageRoomTimelineView_Previews: PreviewProvider, TestablePreview { aspectRatio: 0.7, blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW", contentType: .gif))) + + ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .randomEvent, + timestamp: "Now", + isOutgoing: false, + isEditable: false, + canBeRepliedTo: true, + isThreaded: false, + sender: .init(id: "Bob"), + content: .init(filename: "Blurhashed.jpg", + caption: "This is a great image 😎", + source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), + thumbnailSource: nil, + width: 50, + height: 50, + aspectRatio: 1, + blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW", + contentType: .gif))) } } } diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift index a99980d85b..27b0ddd69e 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift @@ -12,13 +12,30 @@ struct VideoRoomTimelineView: View { @EnvironmentObject private var context: TimelineViewModel.Context let timelineItem: VideoRoomTimelineItem + private var hasMediaCaption: Bool { timelineItem.content.caption != nil } + var body: some View { TimelineStyler(timelineItem: timelineItem) { - thumbnail - .timelineMediaFrame(height: timelineItem.content.height, - aspectRatio: timelineItem.content.aspectRatio) - .accessibilityElement(children: .ignore) - .accessibilityLabel(L10n.commonVideo) + VStack(alignment: .leading, spacing: 4) { + thumbnail + .timelineMediaFrame(height: timelineItem.content.height, + aspectRatio: timelineItem.content.aspectRatio) + .accessibilityElement(children: .ignore) + .accessibilityLabel(L10n.commonVideo) + // This clip shape is distinct from the one in the styler as that one + // operates on the entire message so wouldn't round the bottom corners. + .clipShape(RoundedRectangle(cornerRadius: hasMediaCaption ? 6 : 0)) + + if let attributedCaption = timelineItem.content.formattedCaption { + FormattedBodyText(attributedString: attributedCaption, + additionalWhitespacesCount: timelineItem.additionalWhitespaces(), + boostEmojiSize: true) + } else if let caption = timelineItem.content.caption { + FormattedBodyText(text: caption, + additionalWhitespacesCount: timelineItem.additionalWhitespaces(), + boostEmojiSize: true) + } + } } } @@ -100,6 +117,19 @@ struct VideoRoomTimelineView_Previews: PreviewProvider, TestablePreview { thumbnailSource: nil, aspectRatio: 0.7, blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW"))) + + VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .randomEvent, + timestamp: "Now", + isOutgoing: false, + isEditable: false, + canBeRepliedTo: true, + isThreaded: false, + sender: .init(id: "Bob"), + content: .init(filename: "video.mp4", + caption: "This is a caption", + duration: 21, + source: nil, + thumbnailSource: nil))) } } } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/EventBasedMessageTimelineItemProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineItems/EventBasedMessageTimelineItemProtocol.swift index ce91fe8df0..6b4ae9f5ea 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/EventBasedMessageTimelineItemProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/EventBasedMessageTimelineItemProtocol.swift @@ -24,3 +24,20 @@ protocol EventBasedMessageTimelineItemProtocol: EventBasedTimelineItemProtocol { var contentType: EventBasedMessageTimelineItemContentType { get } var isThreaded: Bool { get } } + +extension EventBasedMessageTimelineItemProtocol { + var hasMediaCaption: Bool { + switch contentType { + case .audio(let content): + content.caption != nil + case .file(let content): + content.caption != nil + case .image(let content): + content.caption != nil + case .video(let content): + content.caption != nil + case .emote, .notice, .text, .location, .voice: + false + } + } +} diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPad-en-GB.1.png index 5469ed0e38..df4ec92cfe 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f63e72b33f82f8b679dd8b1fc810078438914d70d395c23abd13571d01643597 -size 227546 +oid sha256:6cf2fd3de65756ae06e150b5353386e9b5bb554d18da2f77b0cf54246c3920eb +size 285656 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPad-pseudo.1.png index 5469ed0e38..df4ec92cfe 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f63e72b33f82f8b679dd8b1fc810078438914d70d395c23abd13571d01643597 -size 227546 +oid sha256:6cf2fd3de65756ae06e150b5353386e9b5bb554d18da2f77b0cf54246c3920eb +size 285656 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPhone-16-en-GB.1.png index bd08a52887..70f26febce 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb0bce1bf5571926c9ae760503e3aff11bfa962d646e257ab9481bc9496b95a6 -size 169079 +oid sha256:0b2d7c9d9bf570589eb48fbbb11bf11f2970229599a5be9b06d3f54e855e0d06 +size 224889 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPhone-16-pseudo.1.png index bd08a52887..70f26febce 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_imageRoomTimelineView-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb0bce1bf5571926c9ae760503e3aff11bfa962d646e257ab9481bc9496b95a6 -size 169079 +oid sha256:0b2d7c9d9bf570589eb48fbbb11bf11f2970229599a5be9b06d3f54e855e0d06 +size 224889 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPad-en-GB.1.png index 1bc9dd26dc..18df7f1a0b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:919c37cec762f3a5ead4e55e611e78bc3b6a52fdb6c2f2435d387511bdaf3bb3 -size 81177 +oid sha256:f0f9e472dd0a7675c25bc5856aca8f3e8ed94b157aa2dea3683e8a7cf17b588c +size 94892 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPad-pseudo.1.png index 1bc9dd26dc..18df7f1a0b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:919c37cec762f3a5ead4e55e611e78bc3b6a52fdb6c2f2435d387511bdaf3bb3 -size 81177 +oid sha256:f0f9e472dd0a7675c25bc5856aca8f3e8ed94b157aa2dea3683e8a7cf17b588c +size 94892 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPhone-16-en-GB.1.png index 15d62f2135..d3bd5b56d3 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bbf8a9e38c8188356a7e9758b7afbcbc84c4b58b4ada32999e1591883c99704c -size 40312 +oid sha256:8e4e25d87b4335854f8cd67072ef0e8e89a50a9a56dbc10b6cf01ee725731767 +size 52197 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPhone-16-pseudo.1.png index 15d62f2135..d3bd5b56d3 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_videoRoomTimelineView-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bbf8a9e38c8188356a7e9758b7afbcbc84c4b58b4ada32999e1591883c99704c -size 40312 +oid sha256:8e4e25d87b4335854f8cd67072ef0e8e89a50a9a56dbc10b6cf01ee725731767 +size 52197 From a4ea552a83cc0d3e9b23b90e9ec6cdd3ab5b7f33 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 21 Oct 2024 16:07:19 +0100 Subject: [PATCH 070/114] Show a verification badge on the Room Member/User Profile screens. (#3427) * Add a badge for verified users/room members. * Reorder subviews. * Add a (disabled) button to verify other users. * PR comments. * Update the SDK. * Adopt the SDK changes introduced in matrix-rust-sdk/pull/4100 --------- Co-authored-by: Stefan Ceriu --- ElementX.xcodeproj/project.pbxproj | 6 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- ElementX/Sources/Mocks/ClientProxyMock.swift | 2 + .../Mocks/Generated/GeneratedMocks.swift | 70 +++++ .../Mocks/Generated/SDKGeneratedMocks.swift | 250 ++++++++++-------- .../Mocks/SDK/UserIdentitySDKMock.swift | 21 ++ .../SwiftUI/Views/AvatarHeaderView.swift | 33 ++- .../RoomMemberDetailsScreenModels.swift | 13 + .../RoomMemberDetailsScreenViewModel.swift | 54 ++-- .../View/RoomMemberDetailsScreen.swift | 53 ++-- .../ComposerToolbarModels.swift | 7 +- .../ComposerToolbarViewModel.swift | 4 +- .../View/MessageComposer.swift | 2 +- .../Timeline/TimelineInteractionHandler.swift | 2 +- .../Screens/Timeline/TimelineViewModel.swift | 5 +- .../UserProfileScreenModels.swift | 13 + .../UserProfileScreenViewModel.swift | 46 ++-- .../View/UserProfileScreen.swift | 52 +++- .../Sources/Services/Client/ClientProxy.swift | 11 +- .../Services/Client/ClientProxyProtocol.swift | 2 + .../MockRoomTimelineController.swift | 1 - .../RoomTimelineController.swift | 25 +- .../RoomTimelineControllerProtocol.swift | 1 - .../Services/Timeline/TimelineProxy.swift | 18 +- ...st_avatarHeaderView-iPad-en-GB.Members.png | 4 +- ...t_avatarHeaderView-iPad-pseudo.Members.png | 4 +- ...atarHeaderView-iPhone-16-en-GB.Members.png | 4 +- ...tarHeaderView-iPhone-16-pseudo.Members.png | 4 +- ...rDetailsScreen-iPad-en-GB.Ignored-User.png | 4 +- ...berDetailsScreen-iPad-en-GB.Other-User.png | 4 +- ...DetailsScreen-iPad-en-GB.Verified-User.png | 3 + ...DetailsScreen-iPad-pseudo.Ignored-User.png | 4 +- ...erDetailsScreen-iPad-pseudo.Other-User.png | 4 +- ...etailsScreen-iPad-pseudo.Verified-User.png | 3 + ...ilsScreen-iPhone-16-en-GB.Ignored-User.png | 4 +- ...tailsScreen-iPhone-16-en-GB.Other-User.png | 4 +- ...lsScreen-iPhone-16-en-GB.Verified-User.png | 3 + ...lsScreen-iPhone-16-pseudo.Ignored-User.png | 4 +- ...ailsScreen-iPhone-16-pseudo.Other-User.png | 4 +- ...sScreen-iPhone-16-pseudo.Verified-User.png | 3 + ...serProfileScreen-iPad-en-GB.Other-User.png | 4 +- ...ProfileScreen-iPad-en-GB.Verified-User.png | 3 + ...erProfileScreen-iPad-pseudo.Other-User.png | 4 +- ...rofileScreen-iPad-pseudo.Verified-User.png | 3 + ...ofileScreen-iPhone-16-en-GB.Other-User.png | 4 +- ...leScreen-iPhone-16-en-GB.Verified-User.png | 3 + ...fileScreen-iPhone-16-pseudo.Other-User.png | 4 +- ...eScreen-iPhone-16-pseudo.Verified-User.png | 3 + .../ComposerToolbarViewModelTests.swift | 12 +- project.yml | 2 +- 50 files changed, 522 insertions(+), 275 deletions(-) create mode 100644 ElementX/Sources/Mocks/SDK/UserIdentitySDKMock.swift create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Verified-User.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Verified-User.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Verified-User.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Verified-User.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Verified-User.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Verified-User.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Verified-User.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Verified-User.png diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index e68a0d5e07..0e2bed091a 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -899,6 +899,7 @@ C76892321558E75101E68ED6 /* ReadableFrameModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398817652FA8ABAE0A31AC6D /* ReadableFrameModifier.swift */; }; C7774720A4B2E34693E3227C /* RoomNotificationSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8896CDD20CA2D87EA3B848A1 /* RoomNotificationSettingsScreen.swift */; }; C7ABEBECDC513F7887DACF66 /* ProgressMaskModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68010886142843705E342645 /* ProgressMaskModifier.swift */; }; + C7B07EBA0F12B5912DA9BB97 /* UserIdentitySDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8DE9D0D480D087D0F676B52 /* UserIdentitySDKMock.swift */; }; C80E06ED97CE52704A46C148 /* ClientBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1C33355FFB0F0953C35036 /* ClientBuilder.swift */; }; C85C7A201E4CFDA477ACEBEB /* AppLockSetupSettingsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8610C1D21565C950BCA6A454 /* AppLockSetupSettingsScreenViewModelProtocol.swift */; }; C8A9C595038AFA2D707AC8C1 /* NotificationPermissionsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20E69F67D2A70ABD08CA6D54 /* NotificationPermissionsScreenViewModelProtocol.swift */; }; @@ -2221,6 +2222,7 @@ E8A1F98AE670377B20679FF5 /* MediaPlayerProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayerProvider.swift; sourceTree = ""; }; E8AE4B3273BA189FDCD4055C /* UserIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicator.swift; sourceTree = ""; }; E8CA187FE656EE5A3F6C7DE5 /* UIFont+AttributedStringBuilder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIFont+AttributedStringBuilder.m"; sourceTree = ""; }; + E8DE9D0D480D087D0F676B52 /* UserIdentitySDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIdentitySDKMock.swift; sourceTree = ""; }; E96ED747FF90332EA1333C22 /* RoomTimelineItemFixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemFixtures.swift; sourceTree = ""; }; E992D7B8BE54B2AB454613AF /* XCUIElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCUIElement.swift; sourceTree = ""; }; E9A3D3CFA199FA7897364547 /* CallInviteRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallInviteRoomTimelineItem.swift; sourceTree = ""; }; @@ -5276,6 +5278,7 @@ isa = PBXGroup; children = ( 8EAF4A49F3ACD8BB8B0D2371 /* ClientSDKMock.swift */, + E8DE9D0D480D087D0F676B52 /* UserIdentitySDKMock.swift */, ); path = SDK; sourceTree = ""; @@ -7007,6 +7010,7 @@ 828EA5009557C2B9DCD4CA0F /* UserDiscoverySection.swift in Sources */, 044DD8F80231BC30570F7965 /* UserDiscoveryService.swift in Sources */, 1C409A26A99F0371C47AFA51 /* UserDiscoveryServiceProtocol.swift in Sources */, + C7B07EBA0F12B5912DA9BB97 /* UserIdentitySDKMock.swift in Sources */, 988BA75A182738150894A23F /* UserIndicator.swift in Sources */, C4E0D03DF88242697545A9B7 /* UserIndicatorController.swift in Sources */, 3467FEE8210D301FF1B77001 /* UserIndicatorControllerMock.swift in Sources */, @@ -7818,7 +7822,7 @@ repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.0.58; + version = 1.0.59; }; }; 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0c07855fa0..46b5123a2c 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/matrix-rust-components-swift", "state" : { - "revision" : "753c5381ce88b3549cbd8ed9b839109ff143ecdd", - "version" : "1.0.58" + "revision" : "acd36f68f107f608c33b4d5b46be4ddea5537b1f", + "version" : "1.0.59" } }, { diff --git a/ElementX/Sources/Mocks/ClientProxyMock.swift b/ElementX/Sources/Mocks/ClientProxyMock.swift index 507a21b0a9..ac086c1c43 100644 --- a/ElementX/Sources/Mocks/ClientProxyMock.swift +++ b/ElementX/Sources/Mocks/ClientProxyMock.swift @@ -93,5 +93,7 @@ extension ClientProxyMock { return await .joined(JoinedRoomProxyMock(.init(id: room.id, name: room.name))) } + + userIdentityForReturnValue = .success(UserIdentitySDKMock(configuration: .init())) } } diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 039932ee7f..f5d9d148ff 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -4700,6 +4700,76 @@ class ClientProxyMock: ClientProxyProtocol { return resetIdentityReturnValue } } + //MARK: - userIdentity + + var userIdentityForUnderlyingCallsCount = 0 + var userIdentityForCallsCount: Int { + get { + if Thread.isMainThread { + return userIdentityForUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = userIdentityForUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + userIdentityForUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + userIdentityForUnderlyingCallsCount = newValue + } + } + } + } + var userIdentityForCalled: Bool { + return userIdentityForCallsCount > 0 + } + var userIdentityForReceivedUserID: String? + var userIdentityForReceivedInvocations: [String] = [] + + var userIdentityForUnderlyingReturnValue: Result! + var userIdentityForReturnValue: Result! { + get { + if Thread.isMainThread { + return userIdentityForUnderlyingReturnValue + } else { + var returnValue: Result? = nil + DispatchQueue.main.sync { + returnValue = userIdentityForUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + userIdentityForUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + userIdentityForUnderlyingReturnValue = newValue + } + } + } + } + var userIdentityForClosure: ((String) async -> Result)? + + func userIdentity(for userID: String) async -> Result { + userIdentityForCallsCount += 1 + userIdentityForReceivedUserID = userID + DispatchQueue.main.async { + self.userIdentityForReceivedInvocations.append(userID) + } + if let userIdentityForClosure = userIdentityForClosure { + return await userIdentityForClosure(userID) + } else { + return userIdentityForReturnValue + } + } //MARK: - loadMediaContentForSource var loadMediaContentForSourceThrowableError: Error? diff --git a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift index 52c51b578b..968f3fd9c8 100644 --- a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift @@ -6214,81 +6214,6 @@ open class EncryptionSDKMock: MatrixRustSDK.Encryption { } } - //MARK: - getUserIdentity - - open var getUserIdentityUserIdThrowableError: Error? - var getUserIdentityUserIdUnderlyingCallsCount = 0 - open var getUserIdentityUserIdCallsCount: Int { - get { - if Thread.isMainThread { - return getUserIdentityUserIdUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = getUserIdentityUserIdUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - getUserIdentityUserIdUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - getUserIdentityUserIdUnderlyingCallsCount = newValue - } - } - } - } - open var getUserIdentityUserIdCalled: Bool { - return getUserIdentityUserIdCallsCount > 0 - } - open var getUserIdentityUserIdReceivedUserId: String? - open var getUserIdentityUserIdReceivedInvocations: [String] = [] - - var getUserIdentityUserIdUnderlyingReturnValue: UserIdentity? - open var getUserIdentityUserIdReturnValue: UserIdentity? { - get { - if Thread.isMainThread { - return getUserIdentityUserIdUnderlyingReturnValue - } else { - var returnValue: UserIdentity?? = nil - DispatchQueue.main.sync { - returnValue = getUserIdentityUserIdUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - getUserIdentityUserIdUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - getUserIdentityUserIdUnderlyingReturnValue = newValue - } - } - } - } - open var getUserIdentityUserIdClosure: ((String) async throws -> UserIdentity?)? - - open override func getUserIdentity(userId: String) async throws -> UserIdentity? { - if let error = getUserIdentityUserIdThrowableError { - throw error - } - getUserIdentityUserIdCallsCount += 1 - getUserIdentityUserIdReceivedUserId = userId - DispatchQueue.main.async { - self.getUserIdentityUserIdReceivedInvocations.append(userId) - } - if let getUserIdentityUserIdClosure = getUserIdentityUserIdClosure { - return try await getUserIdentityUserIdClosure(userId) - } else { - return getUserIdentityUserIdReturnValue - } - } - //MARK: - isLastDevice open var isLastDeviceThrowableError: Error? @@ -6753,6 +6678,81 @@ open class EncryptionSDKMock: MatrixRustSDK.Encryption { } } + //MARK: - userIdentity + + open var userIdentityUserIdThrowableError: Error? + var userIdentityUserIdUnderlyingCallsCount = 0 + open var userIdentityUserIdCallsCount: Int { + get { + if Thread.isMainThread { + return userIdentityUserIdUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = userIdentityUserIdUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + userIdentityUserIdUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + userIdentityUserIdUnderlyingCallsCount = newValue + } + } + } + } + open var userIdentityUserIdCalled: Bool { + return userIdentityUserIdCallsCount > 0 + } + open var userIdentityUserIdReceivedUserId: String? + open var userIdentityUserIdReceivedInvocations: [String] = [] + + var userIdentityUserIdUnderlyingReturnValue: UserIdentity? + open var userIdentityUserIdReturnValue: UserIdentity? { + get { + if Thread.isMainThread { + return userIdentityUserIdUnderlyingReturnValue + } else { + var returnValue: UserIdentity?? = nil + DispatchQueue.main.sync { + returnValue = userIdentityUserIdUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + userIdentityUserIdUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + userIdentityUserIdUnderlyingReturnValue = newValue + } + } + } + } + open var userIdentityUserIdClosure: ((String) async throws -> UserIdentity?)? + + open override func userIdentity(userId: String) async throws -> UserIdentity? { + if let error = userIdentityUserIdThrowableError { + throw error + } + userIdentityUserIdCallsCount += 1 + userIdentityUserIdReceivedUserId = userId + DispatchQueue.main.async { + self.userIdentityUserIdReceivedInvocations.append(userId) + } + if let userIdentityUserIdClosure = userIdentityUserIdClosure { + return try await userIdentityUserIdClosure(userId) + } else { + return userIdentityUserIdReturnValue + } + } + //MARK: - verificationState var verificationStateUnderlyingCallsCount = 0 @@ -18042,34 +18042,9 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } open var editEventOrTransactionIdNewContentReceivedArguments: (eventOrTransactionId: EventOrTransactionId, newContent: EditedContent)? open var editEventOrTransactionIdNewContentReceivedInvocations: [(eventOrTransactionId: EventOrTransactionId, newContent: EditedContent)] = [] + open var editEventOrTransactionIdNewContentClosure: ((EventOrTransactionId, EditedContent) async throws -> Void)? - var editEventOrTransactionIdNewContentUnderlyingReturnValue: Bool! - open var editEventOrTransactionIdNewContentReturnValue: Bool! { - get { - if Thread.isMainThread { - return editEventOrTransactionIdNewContentUnderlyingReturnValue - } else { - var returnValue: Bool? = nil - DispatchQueue.main.sync { - returnValue = editEventOrTransactionIdNewContentUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - editEventOrTransactionIdNewContentUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - editEventOrTransactionIdNewContentUnderlyingReturnValue = newValue - } - } - } - } - open var editEventOrTransactionIdNewContentClosure: ((EventOrTransactionId, EditedContent) async throws -> Bool)? - - open override func edit(eventOrTransactionId: EventOrTransactionId, newContent: EditedContent) async throws -> Bool { + open override func edit(eventOrTransactionId: EventOrTransactionId, newContent: EditedContent) async throws { if let error = editEventOrTransactionIdNewContentThrowableError { throw error } @@ -18078,11 +18053,7 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { DispatchQueue.main.async { self.editEventOrTransactionIdNewContentReceivedInvocations.append((eventOrTransactionId: eventOrTransactionId, newContent: newContent)) } - if let editEventOrTransactionIdNewContentClosure = editEventOrTransactionIdNewContentClosure { - return try await editEventOrTransactionIdNewContentClosure(eventOrTransactionId, newContent) - } else { - return editEventOrTransactionIdNewContentReturnValue - } + try await editEventOrTransactionIdNewContentClosure?(eventOrTransactionId, newContent) } //MARK: - endPoll @@ -20976,6 +20947,71 @@ open class UserIdentitySDKMock: MatrixRustSDK.UserIdentity { fileprivate var pointer: UnsafeMutableRawPointer! + //MARK: - isVerified + + var isVerifiedUnderlyingCallsCount = 0 + open var isVerifiedCallsCount: Int { + get { + if Thread.isMainThread { + return isVerifiedUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = isVerifiedUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + isVerifiedUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + isVerifiedUnderlyingCallsCount = newValue + } + } + } + } + open var isVerifiedCalled: Bool { + return isVerifiedCallsCount > 0 + } + + var isVerifiedUnderlyingReturnValue: Bool! + open var isVerifiedReturnValue: Bool! { + get { + if Thread.isMainThread { + return isVerifiedUnderlyingReturnValue + } else { + var returnValue: Bool? = nil + DispatchQueue.main.sync { + returnValue = isVerifiedUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + isVerifiedUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + isVerifiedUnderlyingReturnValue = newValue + } + } + } + } + open var isVerifiedClosure: (() -> Bool)? + + open override func isVerified() -> Bool { + isVerifiedCallsCount += 1 + if let isVerifiedClosure = isVerifiedClosure { + return isVerifiedClosure() + } else { + return isVerifiedReturnValue + } + } + //MARK: - masterKey var masterKeyUnderlyingCallsCount = 0 diff --git a/ElementX/Sources/Mocks/SDK/UserIdentitySDKMock.swift b/ElementX/Sources/Mocks/SDK/UserIdentitySDKMock.swift new file mode 100644 index 0000000000..885cf1a418 --- /dev/null +++ b/ElementX/Sources/Mocks/SDK/UserIdentitySDKMock.swift @@ -0,0 +1,21 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Foundation +import MatrixRustSDK + +extension UserIdentitySDKMock { + struct Configuration { + var isVerified = false + } + + convenience init(configuration: Configuration) { + self.init() + + isVerifiedReturnValue = configuration.isVerified + } +} diff --git a/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift b/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift index 26d8443bf6..2717af4a82 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift @@ -17,6 +17,7 @@ struct AvatarHeaderView: View { private enum Badge: Hashable { case encrypted(Bool) case `public` + case verified } private let avatarInfo: AvatarInfo @@ -70,6 +71,7 @@ struct AvatarHeaderView: View { } init(member: RoomMemberDetails, + isVerified: Bool = false, avatarSize: AvatarSize, mediaProvider: MediaProviderProtocol? = nil, onAvatarTap: ((URL) -> Void)? = nil, @@ -77,6 +79,7 @@ struct AvatarHeaderView: View { let profile = UserProfileProxy(member: member) self.init(user: profile, + isVerified: isVerified, avatarSize: avatarSize, mediaProvider: mediaProvider, onAvatarTap: onAvatarTap, @@ -84,6 +87,7 @@ struct AvatarHeaderView: View { } init(user: UserProfileProxy, + isVerified: Bool, avatarSize: AvatarSize, mediaProvider: MediaProviderProtocol? = nil, onAvatarTap: ((URL) -> Void)? = nil, @@ -96,27 +100,29 @@ struct AvatarHeaderView: View { self.mediaProvider = mediaProvider self.onAvatarTap = onAvatarTap self.footer = footer - badges = [] + badges = isVerified ? [.verified] : [] } private var badgesStack: some View { HStack(spacing: 8) { ForEach(badges, id: \.self) { badge in switch badge { - case .encrypted(let isEncrypted): - if isEncrypted { - BadgeLabel(title: L10n.screenRoomDetailsBadgeEncrypted, - icon: \.lockSolid, - isHighlighted: true) - } else { - BadgeLabel(title: L10n.screenRoomDetailsBadgeNotEncrypted, - icon: \.lockOff, - isHighlighted: false) - } + case .encrypted(true): + BadgeLabel(title: L10n.screenRoomDetailsBadgeEncrypted, + icon: \.lockSolid, + isHighlighted: true) + case .encrypted(false): + BadgeLabel(title: L10n.screenRoomDetailsBadgeNotEncrypted, + icon: \.lockOff, + isHighlighted: false) case .public: BadgeLabel(title: L10n.screenRoomDetailsBadgePublic, icon: \.public, isHighlighted: false) + case .verified: + BadgeLabel(title: L10n.commonVerified, + icon: \.verified, + isHighlighted: true) } } } @@ -220,6 +226,11 @@ struct AvatarHeaderView_Previews: PreviewProvider, TestablePreview { avatarSize: .room(on: .details), mediaProvider: MediaProviderMock(configuration: .init())) { Text("") } + AvatarHeaderView(member: RoomMemberDetails(withProxy: RoomMemberProxyMock.mockBob), + isVerified: true, + avatarSize: .room(on: .details), + mediaProvider: MediaProviderMock(configuration: .init())) { Text("") } + AvatarHeaderView(member: RoomMemberDetails(withProxy: RoomMemberProxyMock.mockBanned[3]), avatarSize: .room(on: .details), mediaProvider: MediaProviderMock(configuration: .init())) { Text("") } diff --git a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenModels.swift b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenModels.swift index 0b33b653c8..e55252e65e 100644 --- a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenModels.swift +++ b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenModels.swift @@ -16,11 +16,24 @@ enum RoomMemberDetailsScreenViewModelAction { struct RoomMemberDetailsScreenViewState: BindableState { let userID: String var memberDetails: RoomMemberDetails? + var isVerified: Bool? var isOwnMemberDetails = false var isProcessingIgnoreRequest = false var dmRoomID: String? var bindings: RoomMemberDetailsScreenViewStateBindings + + var showVerifiedBadge: Bool { + isVerified == true // We purposely show the badge on your own account for consistency with Web. + } + + var showVerificationSection: Bool { + isVerified == false && !isOwnMemberDetails + } + + var verifyButtonTitle: String { + L10n.screenRoomMemberDetailsVerifyButtonTitle(memberDetails?.name ?? "") + } } struct RoomMemberDetailsScreenViewStateBindings { diff --git a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModel.swift b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModel.swift index 1bd2dc81d9..178183a853 100644 --- a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModel.swift @@ -43,25 +43,8 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro showMemberLoadingIndicator() Task { - defer { - hideMemberLoadingIndicator() - } - - switch await roomProxy.getMember(userID: userID) { - case .success(let member): - roomMemberProxy = member - state.memberDetails = RoomMemberDetails(withProxy: member) - state.isOwnMemberDetails = member.userID == roomProxy.ownUserID - switch await clientProxy.directRoomForUserID(member.userID) { - case .success(let roomID): - state.dmRoomID = roomID - case .failure: - break - } - case .failure(let error): - MXLog.warning("Failed to find member: \(error)") - actionsSubject.send(.openUserProfile) - } + await loadMember() + hideMemberLoadingIndicator() } } @@ -94,8 +77,37 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro } // MARK: - Private - - @MainActor + + private func loadMember() async { + async let memberResult = roomProxy.getMember(userID: state.userID) + async let identityResult = clientProxy.userIdentity(for: state.userID) + + switch await memberResult { + case .success(let member): + roomMemberProxy = member + state.memberDetails = RoomMemberDetails(withProxy: member) + state.isOwnMemberDetails = member.userID == roomProxy.ownUserID + switch await clientProxy.directRoomForUserID(member.userID) { + case .success(let roomID): + state.dmRoomID = roomID + case .failure: + break + } + case .failure(let error): + MXLog.warning("Failed to find member: \(error)") + // As we didn't find a member with the specified user ID in this room we instead + // fall back to showing a generic user profile screen as the source is likely + // a message containing a permalink to someone who's not in this room. + actionsSubject.send(.openUserProfile) + } + + if case let .success(.some(identity)) = await identityResult { + state.isVerified = identity.isVerified() + } else { + MXLog.error("Failed to find the member's identity.") + } + } + private func ignoreUser() async { guard let roomMemberProxy else { fatalError() diff --git a/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift b/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift index 680aea69e5..0e7be2d2f8 100644 --- a/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift +++ b/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift @@ -15,6 +15,8 @@ struct RoomMemberDetailsScreen: View { Form { headerSection + verificationSection + if context.viewState.memberDetails != nil, !context.viewState.isOwnMemberDetails { blockUserSection } @@ -30,6 +32,25 @@ struct RoomMemberDetailsScreen: View { // MARK: - Private @ViewBuilder + private var headerSection: some View { + if let memberDetails = context.viewState.memberDetails { + AvatarHeaderView(member: memberDetails, + isVerified: context.viewState.showVerifiedBadge, + avatarSize: .user(on: .memberDetails), + mediaProvider: context.mediaProvider) { url in + context.send(viewAction: .displayAvatar(url)) + } footer: { + otherUserFooter + } + } else { + AvatarHeaderView(user: UserProfileProxy(userID: context.viewState.userID), + isVerified: context.viewState.showVerifiedBadge, + avatarSize: .user(on: .memberDetails), + mediaProvider: context.mediaProvider, + footer: { }) + } + } + private var otherUserFooter: some View { HStack(spacing: 8) { if context.viewState.memberDetails != nil, !context.viewState.isOwnMemberDetails { @@ -62,20 +83,15 @@ struct RoomMemberDetailsScreen: View { } @ViewBuilder - private var headerSection: some View { - if let memberDetails = context.viewState.memberDetails { - AvatarHeaderView(member: memberDetails, - avatarSize: .user(on: .memberDetails), - mediaProvider: context.mediaProvider) { url in - context.send(viewAction: .displayAvatar(url)) - } footer: { - otherUserFooter + var verificationSection: some View { + if context.viewState.showVerificationSection { + Section { + ListRow(label: .default(title: context.viewState.verifyButtonTitle, + description: L10n.screenRoomMemberDetailsVerifyButtonSubtitle, + icon: \.lock), + kind: .button { }) + .disabled(true) } - } else { - AvatarHeaderView(user: UserProfileProxy(userID: context.viewState.userID), - avatarSize: .user(on: .memberDetails), - mediaProvider: context.mediaProvider, - footer: { }) } } @@ -117,11 +133,15 @@ struct RoomMemberDetailsScreen: View { // MARK: - Previews struct RoomMemberDetailsScreen_Previews: PreviewProvider, TestablePreview { - static let otherUserViewModel = makeViewModel(member: .mockDan) + static let verifiedUserViewModel = makeViewModel(member: .mockDan) + static let otherUserViewModel = makeViewModel(member: .mockAlice) static let accountOwnerViewModel = makeViewModel(member: .mockMe) static let ignoredUserViewModel = makeViewModel(member: .mockIgnored) static var previews: some View { + RoomMemberDetailsScreen(context: verifiedUserViewModel.context) + .previewDisplayName("Verified User") + .snapshotPreferences(delay: 0.25) RoomMemberDetailsScreen(context: otherUserViewModel.context) .previewDisplayName("Other User") .snapshotPreferences(delay: 0.25) @@ -136,9 +156,12 @@ struct RoomMemberDetailsScreen_Previews: PreviewProvider, TestablePreview { static func makeViewModel(member: RoomMemberProxyMock) -> RoomMemberDetailsScreenViewModel { let roomProxyMock = JoinedRoomProxyMock(.init(name: "")) roomProxyMock.getMemberUserIDReturnValue = .success(member) - let clientProxyMock = ClientProxyMock(.init()) + clientProxyMock.userIdentityForClosure = { userID in + let isVerified = userID == RoomMemberProxyMock.mockDan.userID + return .success(UserIdentitySDKMock(configuration: .init(isVerified: isVerified))) + } // to avoid mock the call state for the account owner test case if member.userID != RoomMemberProxyMock.mockMe.userID { clientProxyMock.directRoomForUserIDReturnValue = .success("roomID") diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift index 8f5fc54e37..3459fb1c81 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift @@ -284,14 +284,9 @@ extension FormatType { } enum ComposerMode: Equatable { - enum EditSource { - case timeline - case draftService - } - case `default` case reply(eventID: String, replyDetails: TimelineItemReplyDetails, isThread: Bool) - case edit(originalEventOrTransactionID: EventOrTransactionId, source: EditSource) + case edit(originalEventOrTransactionID: EventOrTransactionId) case recordVoiceMessage(state: AudioRecorderState) case previewVoiceMessage(state: AudioPlayerState, waveform: WaveformSource, isUploading: Bool) diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift index c15a17b65b..36471da729 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift @@ -258,7 +258,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool case .newMessage: set(mode: .default) case .edit(let eventID): - set(mode: .edit(originalEventOrTransactionID: .eventId(eventId: eventID), source: .draftService)) + set(mode: .edit(originalEventOrTransactionID: .eventId(eventId: eventID))) case .reply(let eventID): set(mode: .reply(eventID: eventID, replyDetails: .loading(eventID: eventID), isThread: false)) replyLoadingTask = Task { @@ -314,7 +314,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool switch state.composerMode { case .default: type = .newMessage - case .edit(.eventId(let originalEventID), _): + case .edit(.eventId(let originalEventID)): type = .edit(eventID: originalEventID) case .reply(let eventID, _, _): type = .reply(eventID: eventID) diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift index 16f2c0f711..0a142b37a1 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift @@ -275,7 +275,7 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview { messageComposer() messageComposer(.init(string: "Some message"), - mode: .edit(originalEventOrTransactionID: .eventId(eventId: UUID().uuidString), source: .timeline)) + mode: .edit(originalEventOrTransactionID: .eventId(eventId: UUID().uuidString))) messageComposer(mode: .reply(eventID: UUID().uuidString, replyDetails: .loaded(sender: .init(id: "Kirk"), diff --git a/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift b/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift index 281183664f..36d3429c32 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift @@ -212,7 +212,7 @@ class TimelineInteractionHandler { } // Always update the mode first and then the text so that the composer has time to save the text draft - actionsSubject.send(.composer(action: .setMode(mode: .edit(originalEventOrTransactionID: eventOrTransactionID, source: .timeline)))) + actionsSubject.send(.composer(action: .setMode(mode: .edit(originalEventOrTransactionID: eventOrTransactionID)))) actionsSubject.send(.composer(action: .setText(plainText: text, htmlText: htmlText))) } diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index 80c2460361..7d35cb7a73 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -595,16 +595,15 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { } actionsSubject.send(.composer(action: .clear)) - + switch mode { case .reply(let eventID, _, _): await timelineController.sendMessage(message, html: html, inReplyToEventID: eventID, intentionalMentions: intentionalMentions) - case .edit(let originalEventOrTransactionID, let source): + case .edit(let originalEventOrTransactionID): await timelineController.edit(originalEventOrTransactionID, - useTimeline: source == .timeline, message: message, html: html, intentionalMentions: intentionalMentions) diff --git a/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenModels.swift b/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenModels.swift index 4cb0647dd8..ba3ffd6e4e 100644 --- a/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenModels.swift +++ b/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenModels.swift @@ -19,10 +19,23 @@ struct UserProfileScreenViewState: BindableState { let isPresentedModally: Bool var userProfile: UserProfileProxy? + var isVerified: Bool? var permalink: URL? var dmRoomID: String? var bindings: UserProfileScreenViewStateBindings + + var showVerifiedBadge: Bool { + isVerified == true // We purposely show the badge on your own account for consistency with Web. + } + + var showVerificationSection: Bool { + isVerified == false && !isOwnUser + } + + var verifyButtonTitle: String { + L10n.screenRoomMemberDetailsVerifyButtonTitle(userProfile?.displayName ?? "") + } } struct UserProfileScreenViewStateBindings { diff --git a/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenViewModel.swift b/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenViewModel.swift index c409a72890..01a0f10337 100644 --- a/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenViewModel.swift +++ b/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenViewModel.swift @@ -42,24 +42,8 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr showLoadingIndicator(allowsInteraction: true) Task { - defer { - hideLoadingIndicator() - } - - switch await clientProxy.profile(for: userID) { - case .success(let userProfile): - state.userProfile = userProfile - state.permalink = (try? matrixToUserPermalink(userId: userID)).flatMap(URL.init(string:)) - switch await clientProxy.directRoomForUserID(userProfile.userID) { - case .success(let roomID): - state.dmRoomID = roomID - case .failure: - break - } - case .failure(let error): - state.bindings.alertInfo = .init(id: .unknown) - MXLog.error("Failed to find user profile: \(error)") - } + await loadProfile() + hideLoadingIndicator() } } @@ -87,6 +71,32 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr // MARK: - Private + private func loadProfile() async { + async let profileResult = clientProxy.profile(for: state.userID) + async let identityResult = clientProxy.userIdentity(for: state.userID) + + switch await profileResult { + case .success(let userProfile): + state.userProfile = userProfile + state.permalink = (try? matrixToUserPermalink(userId: state.userID)).flatMap(URL.init(string:)) + switch await clientProxy.directRoomForUserID(userProfile.userID) { + case .success(let roomID): + state.dmRoomID = roomID + case .failure: + break + } + case .failure(let error): + state.bindings.alertInfo = .init(id: .unknown) + MXLog.error("Failed to find user profile: \(error)") + } + + if case let .success(.some(identity)) = await identityResult { + state.isVerified = identity.isVerified() + } else { + MXLog.error("Failed to find the user's identity.") + } + } + private func displayFullScreenAvatar(_ url: URL) async { guard let userProfile = state.userProfile else { fatalError() } diff --git a/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift b/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift index b8d7f749a7..075b53a51c 100644 --- a/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift +++ b/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift @@ -14,6 +14,8 @@ struct UserProfileScreen: View { var body: some View { Form { headerSection + + verificationSection } .compoundList() .navigationTitle(L10n.screenRoomMemberDetailsTitle) @@ -27,6 +29,25 @@ struct UserProfileScreen: View { // MARK: - Private @ViewBuilder + private var headerSection: some View { + if let userProfile = context.viewState.userProfile { + AvatarHeaderView(user: userProfile, + isVerified: context.viewState.showVerifiedBadge, + avatarSize: .user(on: .memberDetails), + mediaProvider: context.mediaProvider) { url in + context.send(viewAction: .displayAvatar(url)) + } footer: { + otherUserFooter + } + } else { + AvatarHeaderView(user: UserProfileProxy(userID: context.viewState.userID), + isVerified: context.viewState.showVerifiedBadge, + avatarSize: .user(on: .memberDetails), + mediaProvider: context.mediaProvider, + footer: { }) + } + } + private var otherUserFooter: some View { HStack(spacing: 8) { if context.viewState.userProfile != nil, !context.viewState.isOwnUser { @@ -59,20 +80,15 @@ struct UserProfileScreen: View { } @ViewBuilder - private var headerSection: some View { - if let userProfile = context.viewState.userProfile { - AvatarHeaderView(user: userProfile, - avatarSize: .user(on: .memberDetails), - mediaProvider: context.mediaProvider) { url in - context.send(viewAction: .displayAvatar(url)) - } footer: { - otherUserFooter + var verificationSection: some View { + if context.viewState.showVerificationSection { + Section { + ListRow(label: .default(title: context.viewState.verifyButtonTitle, + description: L10n.screenRoomMemberDetailsVerifyButtonSubtitle, + icon: \.lock), + kind: .button { }) + .disabled(true) } - } else { - AvatarHeaderView(user: UserProfileProxy(userID: context.viewState.userID), - avatarSize: .user(on: .memberDetails), - mediaProvider: context.mediaProvider, - footer: { }) } } @@ -91,10 +107,14 @@ struct UserProfileScreen: View { // MARK: - Previews struct UserProfileScreen_Previews: PreviewProvider, TestablePreview { - static let otherUserViewModel = makeViewModel(userID: RoomMemberProxyMock.mockDan.userID) + static let verifiedUserViewModel = makeViewModel(userID: RoomMemberProxyMock.mockDan.userID) + static let otherUserViewModel = makeViewModel(userID: RoomMemberProxyMock.mockAlice.userID) static let accountOwnerViewModel = makeViewModel(userID: RoomMemberProxyMock.mockMe.userID) static var previews: some View { + UserProfileScreen(context: verifiedUserViewModel.context) + .previewDisplayName("Verified User") + .snapshotPreferences(delay: 0.25) UserProfileScreen(context: otherUserViewModel.context) .previewDisplayName("Other User") .snapshotPreferences(delay: 0.25) @@ -105,6 +125,10 @@ struct UserProfileScreen_Previews: PreviewProvider, TestablePreview { static func makeViewModel(userID: String) -> UserProfileScreenViewModel { let clientProxyMock = ClientProxyMock(.init()) + clientProxyMock.userIdentityForClosure = { userID in + let isVerified = userID == RoomMemberProxyMock.mockDan.userID + return .success(UserIdentitySDKMock(configuration: .init(isVerified: isVerified))) + } if userID != RoomMemberProxyMock.mockMe.userID { clientProxyMock.directRoomForUserIDReturnValue = .success("roomID") } diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 06972a1ec7..1551008861 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -933,7 +933,7 @@ class ClientProxy: ClientProxyProtocol { MXLog.info("Pinning current identity for user: \(userID)") do { - guard let userIdentity = try await client.encryption().getUserIdentity(userId: userID) else { + guard let userIdentity = try await client.encryption().userIdentity(userId: userID) else { MXLog.error("Failed retrieving identity for user: \(userID)") return .failure(.failedRetrievingUserIdentity) } @@ -952,6 +952,15 @@ class ClientProxy: ClientProxyProtocol { return .failure(.sdkError(error)) } } + + func userIdentity(for userID: String) async -> Result { + do { + return try await .success(client.encryption().userIdentity(userId: userID)) + } catch { + MXLog.error("Failed retrieving user identity: \(error)") + return .failure(.sdkError(error)) + } + } } extension ClientProxy: MediaLoaderProtocol { diff --git a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift index aa7663a533..876cfcee3a 100644 --- a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift +++ b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift @@ -204,4 +204,6 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol { func pinUserIdentity(_ userID: String) async -> Result func resetIdentity() async -> Result + + func userIdentity(for userID: String) async -> Result } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift index c5d18c0729..e615165936 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift @@ -87,7 +87,6 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol { func toggleReaction(_ reaction: String, to eventID: EventOrTransactionId) async { } func edit(_ eventOrTransactionID: EventOrTransactionId, - useTimeline: Bool, message: String, html: String?, intentionalMentions: IntentionalMentions) async { } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift index f9738a1735..b7e704324c 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift @@ -170,7 +170,6 @@ class RoomTimelineController: RoomTimelineControllerProtocol { } func edit(_ eventOrTransactionID: EventOrTransactionId, - useTimeline: Bool, message: String, html: String?, intentionalMentions: IntentionalMentions) async { @@ -181,25 +180,11 @@ class RoomTimelineController: RoomTimelineControllerProtocol { html: html, intentionalMentions: intentionalMentions.toRustMentions()) - if useTimeline { - switch await activeTimeline.edit(eventOrTransactionID, newContent: messageContent) { - case .success: - MXLog.info("Finished editing message by event") - case let .failure(error): - MXLog.error("Failed editing message by event with error: \(error)") - } - } else { - guard case let .eventId(eventID) = eventOrTransactionID else { - MXLog.error("Failed editing message, missing eventID.") - return - } - - switch await roomProxy.edit(eventID: eventID, newContent: messageContent) { - case .success: - MXLog.info("Finished editing message by event ID") - case let .failure(error): - MXLog.error("Failed editing message by event ID with error: \(error)") - } + switch await activeTimeline.edit(eventOrTransactionID, newContent: messageContent) { + case .success: + MXLog.info("Finished editing message by event") + case let .failure(error): + MXLog.error("Failed editing message by event with error: \(error)") } } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift index 4b47bacf44..125454d3cc 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift @@ -52,7 +52,6 @@ protocol RoomTimelineControllerProtocol { intentionalMentions: IntentionalMentions) async func edit(_ eventOrTransactionID: EventOrTransactionId, - useTimeline: Bool, message: String, html: String?, intentionalMentions: IntentionalMentions) async diff --git a/ElementX/Sources/Services/Timeline/TimelineProxy.swift b/ElementX/Sources/Services/Timeline/TimelineProxy.swift index 160eff7911..659a12d1ea 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxy.swift @@ -166,12 +166,10 @@ final class TimelineProxy: TimelineProxyProtocol { func edit(_ eventOrTransactionID: EventOrTransactionId, newContent: RoomMessageEventContentWithoutRelation) async -> Result { do { - guard try await timeline.edit(eventOrTransactionId: eventOrTransactionID, newContent: .roomMessage(content: newContent)) == true else { - return .failure(.failedEditing) - } + try await timeline.edit(eventOrTransactionId: eventOrTransactionID, newContent: .roomMessage(content: newContent)) MXLog.info("Finished editing timeline item: \(eventOrTransactionID)") - + return .success(()) } catch { MXLog.error("Failed editing timeline item: \(eventOrTransactionID) with error: \(error)") @@ -460,13 +458,11 @@ final class TimelineProxy: TimelineProxyProtocol { do { let originalEvent = try await timeline.getEventTimelineItemByEventId(eventId: eventID) - guard try await timeline.edit(eventOrTransactionId: originalEvent.eventOrTransactionId, - newContent: .pollStart(pollData: .init(question: question, - answers: answers, - maxSelections: 1, - pollKind: .init(pollKind: pollKind)))) else { - return .failure(.failedEditing) - } + try await timeline.edit(eventOrTransactionId: originalEvent.eventOrTransactionId, + newContent: .pollStart(pollData: .init(question: question, + answers: answers, + maxSelections: 1, + pollKind: .init(pollKind: pollKind)))) MXLog.info("Finished editing poll with eventID: \(eventID)") diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPad-en-GB.Members.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPad-en-GB.Members.png index d4a360cab6..eb6d007ce4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPad-en-GB.Members.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPad-en-GB.Members.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b32488a5a7c90b0c4b750d8cd822153782fa8493ac84d54af3496086a856d530 -size 65677 +oid sha256:347ca4459368e8a76e3e5952ef0cdbbb76c89e099cd48f726a11bfa341b32c57 +size 106299 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPad-pseudo.Members.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPad-pseudo.Members.png index d4a360cab6..cd747a2b50 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPad-pseudo.Members.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPad-pseudo.Members.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b32488a5a7c90b0c4b750d8cd822153782fa8493ac84d54af3496086a856d530 -size 65677 +oid sha256:80805199a27b76e9e958197da199105217920310803d839e1347463e0f48e690 +size 106516 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPhone-16-en-GB.Members.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPhone-16-en-GB.Members.png index 932e29de00..65489b2f14 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPhone-16-en-GB.Members.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPhone-16-en-GB.Members.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7dbc686c75e95b8b0fbabad6f707d23bb5f0f5a6f37db69a98fd7a0138b4d0f3 -size 39633 +oid sha256:c039a8c3cb9967aa355ef8472e30469e053a88f08b232df77bc1e1129ff58d60 +size 64744 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPhone-16-pseudo.Members.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPhone-16-pseudo.Members.png index 932e29de00..4057f2f60c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPhone-16-pseudo.Members.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPhone-16-pseudo.Members.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7dbc686c75e95b8b0fbabad6f707d23bb5f0f5a6f37db69a98fd7a0138b4d0f3 -size 39633 +oid sha256:246a69ad32fdbd7a2cae5851b9152d21cdf9263b0746cdbb9c3f2cbf38edd9cc +size 64987 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Ignored-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Ignored-User.png index 6b79b99a5f..d89dcfc938 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Ignored-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Ignored-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:51a163146eacfa1147d3ca6b08a6f713c1d6e466ce2f990b7339a88edadc4bb9 -size 109846 +oid sha256:c4a9d05d2aea48c83dd1f0891eccbdc2d99a9b38176971efd097597e69fda28f +size 121678 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Other-User.png index 193397aad6..c820c177a8 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:057db6fe0f5d5a95482906bbe069f25ceeab1c8f5cc173f3c94014ac84fd828e -size 147921 +oid sha256:3f2806abf81f7d716dd4a740bde06ca6e7d488db513f42bcb70ca4ef8a5c2505 +size 120435 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Verified-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Verified-User.png new file mode 100644 index 0000000000..a4b9b6b419 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Verified-User.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf3638bf8eabd4c30cf9cc67bd55a71d3d7968dba02913b6a1c285b66abe86ca +size 151472 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Ignored-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Ignored-User.png index 9ce01526b5..1775940a9a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Ignored-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Ignored-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2abc9dd95da1d42cc4e20163504253e48079a96253f0f6b020c9214cab3fc177 -size 111394 +oid sha256:b9d5ad23bdf44dbe689d44c03ad2a6f12e993bcf6d8245d614b3d9e3b004b8b9 +size 127141 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Other-User.png index 61ffbcf25a..e2b6c81cd0 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce62f3154f37ec185e2323765a4191c6afe165fbb054f7638ed59d8617fff3e8 -size 149231 +oid sha256:1dcb000d6d4caae5edbfef5e9007d327b72332708a98fc1df54a35622f8eb220 +size 125657 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Verified-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Verified-User.png new file mode 100644 index 0000000000..b954c48cde --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Verified-User.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0d0880d9fa7868f87803bf1a212194a7ca365ed7f10e85ab71561c19f735b121 +size 152996 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Ignored-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Ignored-User.png index d93686c93d..a2845556dd 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Ignored-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Ignored-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9cf7ca6c0fa48a76bca67226963823759bd14dc871dea6a5eb373533d7d6b0e5 -size 61377 +oid sha256:baa7127ec68c63490624fba7d6204063cdf950e941a55066291766e887c2281c +size 71929 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Other-User.png index 41c5c0a1a3..858e2bfb03 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a8128c33e14930422fd2047b329c2a496a2d561a45d4bbafc674013d57c6716 -size 93275 +oid sha256:de7639ceab51d7eb8306c7df3a9a6899c1e78a120e22358908f95f9791cb417f +size 70569 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Verified-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Verified-User.png new file mode 100644 index 0000000000..ec0c34d4ca --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Verified-User.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b065c8fb99d1a1f5045db9cc52b328d1f2a48c3c062349c2d0348a28eec9640 +size 96032 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Ignored-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Ignored-User.png index 0e6d76799a..5920bc5e6a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Ignored-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Ignored-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d16eb4167974cc8231caaf02a91151eb22eab006953e0e43d8da3f047e32652 -size 68228 +oid sha256:d624776c5e6a14ddb5c21e7349a9e2b29499d45137c4dbe906f3ad1014d9c6d2 +size 84082 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Other-User.png index b0a0da8f1a..d1c0729d73 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:35e4e002682e32a283edf58eb593463813b7a78d24bb17635eb07ce6d0365698 -size 100293 +oid sha256:ddcf366a4fea427cd38681cff49842d7f5bc73ebc218efbe7ea9bc5914ae7b62 +size 82982 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Verified-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Verified-User.png new file mode 100644 index 0000000000..c9274153cd --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Verified-User.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:abbeec988102eb396909234e06388c1ab21c6f61875789722ab85a29cca1cc6a +size 104122 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Other-User.png index 718f7844e8..4ca05e10c7 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a5d377d251dfe4cd7303973b586786db8a1185bd11061c31d78c5ab910a11c5 -size 101504 +oid sha256:d5995f233036158f3adaff3e86899e86ccace5c6b6991626c78e1514eb63c5d5 +size 115587 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Verified-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Verified-User.png new file mode 100644 index 0000000000..ca8377be1a --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Verified-User.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cfc307f7565b4f1e5cdef15055090205d69dabf36b19b3f538a331444e1cede +size 104896 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Other-User.png index 1c291cf490..c750e0cb89 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:903d91265fa95a4ad40c4131a90680e805c895e1f57b846838b317200b5e14cd -size 102481 +oid sha256:207bad6ef21783ebcc7c8189af09c233d191b01ddf7dc2fd92d3bbd1df1dd2ae +size 120261 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Verified-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Verified-User.png new file mode 100644 index 0000000000..a4c0e35fb1 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Verified-User.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71303b4e0a9a1757e7658b0490a90d1cf438aade4466402e8777de94afa6d905 +size 106067 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Other-User.png index f359d9eb43..892b09ce16 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2733defffabec358c0d86d9cbade1aba498c312db2c7f93eb045085d8ebb83c -size 53177 +oid sha256:5bd27ed946027cecc6286c1b4a0212b92addba260e7a17a650bb1f8a00ac0df9 +size 65717 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Verified-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Verified-User.png new file mode 100644 index 0000000000..cc46530ed3 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Verified-User.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b4e47701bd9f9b3829f2d5a131aba025ab90a6c0a4b6e06ccfabcbf73e87c87 +size 55937 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Other-User.png index c78221e885..3b7dc05d9c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ce7e2162f0210a0d8ed409e089f7ab5dd62427c45728f5b543c24a5d70f7c6b -size 58590 +oid sha256:a5dca5794cc839ed0d28f7868e044ef5aba075e5e44e0b3e43c9b70df7e642b8 +size 78100 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Verified-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Verified-User.png new file mode 100644 index 0000000000..41c40c9880 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Verified-User.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0412eab0f79b3baa004db621bf76b824c6817c2b90b7d7f8f4ab18f94bb2315 +size 62260 diff --git a/UnitTests/Sources/ComposerToolbarViewModelTests.swift b/UnitTests/Sources/ComposerToolbarViewModelTests.swift index 49aa528bfa..9779ae8bf0 100644 --- a/UnitTests/Sources/ComposerToolbarViewModelTests.swift +++ b/UnitTests/Sources/ComposerToolbarViewModelTests.swift @@ -41,14 +41,14 @@ class ComposerToolbarViewModelTests: XCTestCase { } func testComposerFocus() { - viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), source: .timeline))) + viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: "mock")))) XCTAssertTrue(viewModel.state.bindings.composerFocused) viewModel.process(timelineAction: .removeFocus) XCTAssertFalse(viewModel.state.bindings.composerFocused) } func testComposerMode() { - let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), source: .timeline) + let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventId(eventId: "mock")) viewModel.process(timelineAction: .setMode(mode: mode)) XCTAssertEqual(viewModel.state.composerMode, mode) viewModel.process(timelineAction: .clear) @@ -56,7 +56,7 @@ class ComposerToolbarViewModelTests: XCTestCase { } func testComposerModeIsPublished() { - let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), source: .timeline) + let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventId(eventId: "mock")) let expectation = expectation(description: "Composer mode is published") let cancellable = viewModel .context @@ -236,7 +236,7 @@ class ComposerToolbarViewModelTests: XCTestCase { } viewModel.context.composerFormattingEnabled = false - viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: "testID"), source: .timeline))) + viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: "testID")))) viewModel.context.plainComposerText = .init(string: "Hello world!") viewModel.saveDraft() @@ -395,7 +395,7 @@ class ComposerToolbarViewModelTests: XCTestCase { await fulfillment(of: [expectation], timeout: 10) XCTAssertFalse(viewModel.context.composerFormattingEnabled) - XCTAssertEqual(viewModel.state.composerMode, .edit(originalEventOrTransactionID: .eventId(eventId: "testID"), source: .draftService)) + XCTAssertEqual(viewModel.state.composerMode, .edit(originalEventOrTransactionID: .eventId(eventId: "testID"))) XCTAssertEqual(viewModel.context.plainComposerText, NSAttributedString(string: "Hello world!")) } @@ -483,7 +483,7 @@ class ComposerToolbarViewModelTests: XCTestCase { func testSaveVolatileDraftWhenEditing() { viewModel.context.composerFormattingEnabled = false viewModel.context.plainComposerText = .init(string: "Hello world!") - viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: UUID().uuidString), source: .timeline))) + viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: UUID().uuidString)))) let draft = draftServiceMock.saveVolatileDraftReceivedDraft XCTAssertNotNil(draft) diff --git a/project.yml b/project.yml index f7fde5a70e..adbf58d8a6 100644 --- a/project.yml +++ b/project.yml @@ -60,7 +60,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/element-hq/matrix-rust-components-swift - exactVersion: 1.0.58 + exactVersion: 1.0.59 # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios From 8f9f8cbed0f839c15b460954cb0c2d108a4e829a Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Tue, 22 Oct 2024 17:07:33 +0300 Subject: [PATCH 071/114] Adopt the changes introduced in matrix-org/matrix-rust-sdk/pull/4159 --- ElementX.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- .../Mocks/Generated/SDKGeneratedMocks.swift | 351 +++++++----------- .../Sources/Services/Client/ClientProxy.swift | 2 - .../Services/Client/ClientProxyProtocol.swift | 9 - .../Services/Room/JoinedRoomProxy.swift | 6 +- .../RoomSummary/RoomSummaryProvider.swift | 5 +- .../Services/Timeline/TimelineProxy.swift | 56 ++- project.yml | 2 +- 9 files changed, 186 insertions(+), 251 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 0e2bed091a..a3de5cd3b3 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7822,7 +7822,7 @@ repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.0.59; + version = 1.0.60; }; }; 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 46b5123a2c..c011f4376f 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/matrix-rust-components-swift", "state" : { - "revision" : "acd36f68f107f608c33b4d5b46be4ddea5537b1f", - "version" : "1.0.59" + "revision" : "dc3a2199b0e87824ccf06d1207487d2e49c5e584", + "version" : "1.0.60" } }, { diff --git a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift index 968f3fd9c8..efa0f5bb26 100644 --- a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift @@ -16007,16 +16007,16 @@ open class RoomListServiceSDKMock: MatrixRustSDK.RoomListService { //MARK: - subscribeToRooms - open var subscribeToRoomsRoomIdsSettingsThrowableError: Error? - var subscribeToRoomsRoomIdsSettingsUnderlyingCallsCount = 0 - open var subscribeToRoomsRoomIdsSettingsCallsCount: Int { + open var subscribeToRoomsRoomIdsThrowableError: Error? + var subscribeToRoomsRoomIdsUnderlyingCallsCount = 0 + open var subscribeToRoomsRoomIdsCallsCount: Int { get { if Thread.isMainThread { - return subscribeToRoomsRoomIdsSettingsUnderlyingCallsCount + return subscribeToRoomsRoomIdsUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = subscribeToRoomsRoomIdsSettingsUnderlyingCallsCount + returnValue = subscribeToRoomsRoomIdsUnderlyingCallsCount } return returnValue! @@ -16024,31 +16024,31 @@ open class RoomListServiceSDKMock: MatrixRustSDK.RoomListService { } set { if Thread.isMainThread { - subscribeToRoomsRoomIdsSettingsUnderlyingCallsCount = newValue + subscribeToRoomsRoomIdsUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - subscribeToRoomsRoomIdsSettingsUnderlyingCallsCount = newValue + subscribeToRoomsRoomIdsUnderlyingCallsCount = newValue } } } } - open var subscribeToRoomsRoomIdsSettingsCalled: Bool { - return subscribeToRoomsRoomIdsSettingsCallsCount > 0 + open var subscribeToRoomsRoomIdsCalled: Bool { + return subscribeToRoomsRoomIdsCallsCount > 0 } - open var subscribeToRoomsRoomIdsSettingsReceivedArguments: (roomIds: [String], settings: RoomSubscription?)? - open var subscribeToRoomsRoomIdsSettingsReceivedInvocations: [(roomIds: [String], settings: RoomSubscription?)] = [] - open var subscribeToRoomsRoomIdsSettingsClosure: (([String], RoomSubscription?) throws -> Void)? + open var subscribeToRoomsRoomIdsReceivedRoomIds: [String]? + open var subscribeToRoomsRoomIdsReceivedInvocations: [[String]] = [] + open var subscribeToRoomsRoomIdsClosure: (([String]) throws -> Void)? - open override func subscribeToRooms(roomIds: [String], settings: RoomSubscription?) throws { - if let error = subscribeToRoomsRoomIdsSettingsThrowableError { + open override func subscribeToRooms(roomIds: [String]) throws { + if let error = subscribeToRoomsRoomIdsThrowableError { throw error } - subscribeToRoomsRoomIdsSettingsCallsCount += 1 - subscribeToRoomsRoomIdsSettingsReceivedArguments = (roomIds: roomIds, settings: settings) + subscribeToRoomsRoomIdsCallsCount += 1 + subscribeToRoomsRoomIdsReceivedRoomIds = roomIds DispatchQueue.main.async { - self.subscribeToRoomsRoomIdsSettingsReceivedInvocations.append((roomIds: roomIds, settings: settings)) + self.subscribeToRoomsRoomIdsReceivedInvocations.append(roomIds) } - try subscribeToRoomsRoomIdsSettingsClosure?(roomIds, settings) + try subscribeToRoomsRoomIdsClosure?(roomIds) } //MARK: - syncIndicator @@ -18334,81 +18334,6 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } } - //MARK: - getEventTimelineItemByTransactionId - - open var getEventTimelineItemByTransactionIdTransactionIdThrowableError: Error? - var getEventTimelineItemByTransactionIdTransactionIdUnderlyingCallsCount = 0 - open var getEventTimelineItemByTransactionIdTransactionIdCallsCount: Int { - get { - if Thread.isMainThread { - return getEventTimelineItemByTransactionIdTransactionIdUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = getEventTimelineItemByTransactionIdTransactionIdUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - getEventTimelineItemByTransactionIdTransactionIdUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - getEventTimelineItemByTransactionIdTransactionIdUnderlyingCallsCount = newValue - } - } - } - } - open var getEventTimelineItemByTransactionIdTransactionIdCalled: Bool { - return getEventTimelineItemByTransactionIdTransactionIdCallsCount > 0 - } - open var getEventTimelineItemByTransactionIdTransactionIdReceivedTransactionId: String? - open var getEventTimelineItemByTransactionIdTransactionIdReceivedInvocations: [String] = [] - - var getEventTimelineItemByTransactionIdTransactionIdUnderlyingReturnValue: EventTimelineItem! - open var getEventTimelineItemByTransactionIdTransactionIdReturnValue: EventTimelineItem! { - get { - if Thread.isMainThread { - return getEventTimelineItemByTransactionIdTransactionIdUnderlyingReturnValue - } else { - var returnValue: EventTimelineItem? = nil - DispatchQueue.main.sync { - returnValue = getEventTimelineItemByTransactionIdTransactionIdUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - getEventTimelineItemByTransactionIdTransactionIdUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - getEventTimelineItemByTransactionIdTransactionIdUnderlyingReturnValue = newValue - } - } - } - } - open var getEventTimelineItemByTransactionIdTransactionIdClosure: ((String) async throws -> EventTimelineItem)? - - open override func getEventTimelineItemByTransactionId(transactionId: String) async throws -> EventTimelineItem { - if let error = getEventTimelineItemByTransactionIdTransactionIdThrowableError { - throw error - } - getEventTimelineItemByTransactionIdTransactionIdCallsCount += 1 - getEventTimelineItemByTransactionIdTransactionIdReceivedTransactionId = transactionId - DispatchQueue.main.async { - self.getEventTimelineItemByTransactionIdTransactionIdReceivedInvocations.append(transactionId) - } - if let getEventTimelineItemByTransactionIdTransactionIdClosure = getEventTimelineItemByTransactionIdTransactionIdClosure { - return try await getEventTimelineItemByTransactionIdTransactionIdClosure(transactionId) - } else { - return getEventTimelineItemByTransactionIdTransactionIdReturnValue - } - } - //MARK: - loadReplyDetails open var loadReplyDetailsEventIdStrThrowableError: Error? @@ -18845,15 +18770,15 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { //MARK: - sendAudio - var sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount = 0 - open var sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherCallsCount: Int { + var sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = 0 + open var sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount: Int { get { if Thread.isMainThread { - return sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount + return sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount + returnValue = sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount } return returnValue! @@ -18861,29 +18786,29 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount = newValue + sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount = newValue + sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = newValue } } } } - open var sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherCalled: Bool { - return sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherCallsCount > 0 + open var sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCalled: Bool { + return sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount > 0 } - open var sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherReceivedArguments: (url: String, audioInfo: AudioInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?)? - open var sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherReceivedInvocations: [(url: String, audioInfo: AudioInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?)] = [] + open var sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedArguments: (url: String, audioInfo: AudioInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?)? + open var sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedInvocations: [(url: String, audioInfo: AudioInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?)] = [] - var sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue: SendAttachmentJoinHandle! - open var sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherReturnValue: SendAttachmentJoinHandle! { + var sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue: SendAttachmentJoinHandle! + open var sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReturnValue: SendAttachmentJoinHandle! { get { if Thread.isMainThread { - return sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue + return sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue } else { var returnValue: SendAttachmentJoinHandle? = nil DispatchQueue.main.sync { - returnValue = sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue + returnValue = sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue } return returnValue! @@ -18891,40 +18816,40 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue = newValue + sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue = newValue + sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue = newValue } } } } - open var sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherClosure: ((String, AudioInfo, String?, FormattedBody?, ProgressWatcher?) -> SendAttachmentJoinHandle)? + open var sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure: ((String, AudioInfo, String?, FormattedBody?, Bool, ProgressWatcher?) -> SendAttachmentJoinHandle)? - open override func sendAudio(url: String, audioInfo: AudioInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?) -> SendAttachmentJoinHandle { - sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherCallsCount += 1 - sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherReceivedArguments = (url: url, audioInfo: audioInfo, caption: caption, formattedCaption: formattedCaption, progressWatcher: progressWatcher) + open override func sendAudio(url: String, audioInfo: AudioInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?) -> SendAttachmentJoinHandle { + sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount += 1 + sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedArguments = (url: url, audioInfo: audioInfo, caption: caption, formattedCaption: formattedCaption, storeInCache: storeInCache, progressWatcher: progressWatcher) DispatchQueue.main.async { - self.sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherReceivedInvocations.append((url: url, audioInfo: audioInfo, caption: caption, formattedCaption: formattedCaption, progressWatcher: progressWatcher)) + self.sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedInvocations.append((url: url, audioInfo: audioInfo, caption: caption, formattedCaption: formattedCaption, storeInCache: storeInCache, progressWatcher: progressWatcher)) } - if let sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherClosure = sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherClosure { - return sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherClosure(url, audioInfo, caption, formattedCaption, progressWatcher) + if let sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure = sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure { + return sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure(url, audioInfo, caption, formattedCaption, storeInCache, progressWatcher) } else { - return sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherReturnValue + return sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReturnValue } } //MARK: - sendFile - var sendFileUrlFileInfoProgressWatcherUnderlyingCallsCount = 0 - open var sendFileUrlFileInfoProgressWatcherCallsCount: Int { + var sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingCallsCount = 0 + open var sendFileUrlFileInfoStoreInCacheProgressWatcherCallsCount: Int { get { if Thread.isMainThread { - return sendFileUrlFileInfoProgressWatcherUnderlyingCallsCount + return sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = sendFileUrlFileInfoProgressWatcherUnderlyingCallsCount + returnValue = sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingCallsCount } return returnValue! @@ -18932,29 +18857,29 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendFileUrlFileInfoProgressWatcherUnderlyingCallsCount = newValue + sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - sendFileUrlFileInfoProgressWatcherUnderlyingCallsCount = newValue + sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingCallsCount = newValue } } } } - open var sendFileUrlFileInfoProgressWatcherCalled: Bool { - return sendFileUrlFileInfoProgressWatcherCallsCount > 0 + open var sendFileUrlFileInfoStoreInCacheProgressWatcherCalled: Bool { + return sendFileUrlFileInfoStoreInCacheProgressWatcherCallsCount > 0 } - open var sendFileUrlFileInfoProgressWatcherReceivedArguments: (url: String, fileInfo: FileInfo, progressWatcher: ProgressWatcher?)? - open var sendFileUrlFileInfoProgressWatcherReceivedInvocations: [(url: String, fileInfo: FileInfo, progressWatcher: ProgressWatcher?)] = [] + open var sendFileUrlFileInfoStoreInCacheProgressWatcherReceivedArguments: (url: String, fileInfo: FileInfo, storeInCache: Bool, progressWatcher: ProgressWatcher?)? + open var sendFileUrlFileInfoStoreInCacheProgressWatcherReceivedInvocations: [(url: String, fileInfo: FileInfo, storeInCache: Bool, progressWatcher: ProgressWatcher?)] = [] - var sendFileUrlFileInfoProgressWatcherUnderlyingReturnValue: SendAttachmentJoinHandle! - open var sendFileUrlFileInfoProgressWatcherReturnValue: SendAttachmentJoinHandle! { + var sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingReturnValue: SendAttachmentJoinHandle! + open var sendFileUrlFileInfoStoreInCacheProgressWatcherReturnValue: SendAttachmentJoinHandle! { get { if Thread.isMainThread { - return sendFileUrlFileInfoProgressWatcherUnderlyingReturnValue + return sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingReturnValue } else { var returnValue: SendAttachmentJoinHandle? = nil DispatchQueue.main.sync { - returnValue = sendFileUrlFileInfoProgressWatcherUnderlyingReturnValue + returnValue = sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingReturnValue } return returnValue! @@ -18962,40 +18887,40 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendFileUrlFileInfoProgressWatcherUnderlyingReturnValue = newValue + sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - sendFileUrlFileInfoProgressWatcherUnderlyingReturnValue = newValue + sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingReturnValue = newValue } } } } - open var sendFileUrlFileInfoProgressWatcherClosure: ((String, FileInfo, ProgressWatcher?) -> SendAttachmentJoinHandle)? + open var sendFileUrlFileInfoStoreInCacheProgressWatcherClosure: ((String, FileInfo, Bool, ProgressWatcher?) -> SendAttachmentJoinHandle)? - open override func sendFile(url: String, fileInfo: FileInfo, progressWatcher: ProgressWatcher?) -> SendAttachmentJoinHandle { - sendFileUrlFileInfoProgressWatcherCallsCount += 1 - sendFileUrlFileInfoProgressWatcherReceivedArguments = (url: url, fileInfo: fileInfo, progressWatcher: progressWatcher) + open override func sendFile(url: String, fileInfo: FileInfo, storeInCache: Bool, progressWatcher: ProgressWatcher?) -> SendAttachmentJoinHandle { + sendFileUrlFileInfoStoreInCacheProgressWatcherCallsCount += 1 + sendFileUrlFileInfoStoreInCacheProgressWatcherReceivedArguments = (url: url, fileInfo: fileInfo, storeInCache: storeInCache, progressWatcher: progressWatcher) DispatchQueue.main.async { - self.sendFileUrlFileInfoProgressWatcherReceivedInvocations.append((url: url, fileInfo: fileInfo, progressWatcher: progressWatcher)) + self.sendFileUrlFileInfoStoreInCacheProgressWatcherReceivedInvocations.append((url: url, fileInfo: fileInfo, storeInCache: storeInCache, progressWatcher: progressWatcher)) } - if let sendFileUrlFileInfoProgressWatcherClosure = sendFileUrlFileInfoProgressWatcherClosure { - return sendFileUrlFileInfoProgressWatcherClosure(url, fileInfo, progressWatcher) + if let sendFileUrlFileInfoStoreInCacheProgressWatcherClosure = sendFileUrlFileInfoStoreInCacheProgressWatcherClosure { + return sendFileUrlFileInfoStoreInCacheProgressWatcherClosure(url, fileInfo, storeInCache, progressWatcher) } else { - return sendFileUrlFileInfoProgressWatcherReturnValue + return sendFileUrlFileInfoStoreInCacheProgressWatcherReturnValue } } //MARK: - sendImage - var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount = 0 - open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherCallsCount: Int { + var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = 0 + open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount: Int { get { if Thread.isMainThread { - return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount + return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount + returnValue = sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount } return returnValue! @@ -19003,29 +18928,29 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount = newValue + sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount = newValue + sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = newValue } } } } - open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherCalled: Bool { - return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherCallsCount > 0 + open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCalled: Bool { + return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount > 0 } - open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherReceivedArguments: (url: String, thumbnailUrl: String?, imageInfo: ImageInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?)? - open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherReceivedInvocations: [(url: String, thumbnailUrl: String?, imageInfo: ImageInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?)] = [] + open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedArguments: (url: String, thumbnailUrl: String?, imageInfo: ImageInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?)? + open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedInvocations: [(url: String, thumbnailUrl: String?, imageInfo: ImageInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?)] = [] - var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue: SendAttachmentJoinHandle! - open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherReturnValue: SendAttachmentJoinHandle! { + var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue: SendAttachmentJoinHandle! + open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReturnValue: SendAttachmentJoinHandle! { get { if Thread.isMainThread { - return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue + return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue } else { var returnValue: SendAttachmentJoinHandle? = nil DispatchQueue.main.sync { - returnValue = sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue + returnValue = sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue } return returnValue! @@ -19033,26 +18958,26 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue = newValue + sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue = newValue + sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue = newValue } } } } - open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherClosure: ((String, String?, ImageInfo, String?, FormattedBody?, ProgressWatcher?) -> SendAttachmentJoinHandle)? + open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure: ((String, String?, ImageInfo, String?, FormattedBody?, Bool, ProgressWatcher?) -> SendAttachmentJoinHandle)? - open override func sendImage(url: String, thumbnailUrl: String?, imageInfo: ImageInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?) -> SendAttachmentJoinHandle { - sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherCallsCount += 1 - sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherReceivedArguments = (url: url, thumbnailUrl: thumbnailUrl, imageInfo: imageInfo, caption: caption, formattedCaption: formattedCaption, progressWatcher: progressWatcher) + open override func sendImage(url: String, thumbnailUrl: String?, imageInfo: ImageInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?) -> SendAttachmentJoinHandle { + sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount += 1 + sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedArguments = (url: url, thumbnailUrl: thumbnailUrl, imageInfo: imageInfo, caption: caption, formattedCaption: formattedCaption, storeInCache: storeInCache, progressWatcher: progressWatcher) DispatchQueue.main.async { - self.sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherReceivedInvocations.append((url: url, thumbnailUrl: thumbnailUrl, imageInfo: imageInfo, caption: caption, formattedCaption: formattedCaption, progressWatcher: progressWatcher)) + self.sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedInvocations.append((url: url, thumbnailUrl: thumbnailUrl, imageInfo: imageInfo, caption: caption, formattedCaption: formattedCaption, storeInCache: storeInCache, progressWatcher: progressWatcher)) } - if let sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherClosure = sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherClosure { - return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherClosure(url, thumbnailUrl, imageInfo, caption, formattedCaption, progressWatcher) + if let sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure = sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure { + return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure(url, thumbnailUrl, imageInfo, caption, formattedCaption, storeInCache, progressWatcher) } else { - return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherReturnValue + return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReturnValue } } @@ -19238,15 +19163,15 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { //MARK: - sendVideo - var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount = 0 - open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherCallsCount: Int { + var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = 0 + open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount: Int { get { if Thread.isMainThread { - return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount + return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount + returnValue = sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount } return returnValue! @@ -19254,29 +19179,29 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount = newValue + sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount = newValue + sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = newValue } } } } - open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherCalled: Bool { - return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherCallsCount > 0 + open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCalled: Bool { + return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount > 0 } - open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherReceivedArguments: (url: String, thumbnailUrl: String?, videoInfo: VideoInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?)? - open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherReceivedInvocations: [(url: String, thumbnailUrl: String?, videoInfo: VideoInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?)] = [] + open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedArguments: (url: String, thumbnailUrl: String?, videoInfo: VideoInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?)? + open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedInvocations: [(url: String, thumbnailUrl: String?, videoInfo: VideoInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?)] = [] - var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue: SendAttachmentJoinHandle! - open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherReturnValue: SendAttachmentJoinHandle! { + var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue: SendAttachmentJoinHandle! + open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReturnValue: SendAttachmentJoinHandle! { get { if Thread.isMainThread { - return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue + return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue } else { var returnValue: SendAttachmentJoinHandle? = nil DispatchQueue.main.sync { - returnValue = sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue + returnValue = sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue } return returnValue! @@ -19284,40 +19209,40 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue = newValue + sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue = newValue + sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue = newValue } } } } - open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherClosure: ((String, String?, VideoInfo, String?, FormattedBody?, ProgressWatcher?) -> SendAttachmentJoinHandle)? + open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure: ((String, String?, VideoInfo, String?, FormattedBody?, Bool, ProgressWatcher?) -> SendAttachmentJoinHandle)? - open override func sendVideo(url: String, thumbnailUrl: String?, videoInfo: VideoInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?) -> SendAttachmentJoinHandle { - sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherCallsCount += 1 - sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherReceivedArguments = (url: url, thumbnailUrl: thumbnailUrl, videoInfo: videoInfo, caption: caption, formattedCaption: formattedCaption, progressWatcher: progressWatcher) + open override func sendVideo(url: String, thumbnailUrl: String?, videoInfo: VideoInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?) -> SendAttachmentJoinHandle { + sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount += 1 + sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedArguments = (url: url, thumbnailUrl: thumbnailUrl, videoInfo: videoInfo, caption: caption, formattedCaption: formattedCaption, storeInCache: storeInCache, progressWatcher: progressWatcher) DispatchQueue.main.async { - self.sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherReceivedInvocations.append((url: url, thumbnailUrl: thumbnailUrl, videoInfo: videoInfo, caption: caption, formattedCaption: formattedCaption, progressWatcher: progressWatcher)) + self.sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedInvocations.append((url: url, thumbnailUrl: thumbnailUrl, videoInfo: videoInfo, caption: caption, formattedCaption: formattedCaption, storeInCache: storeInCache, progressWatcher: progressWatcher)) } - if let sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherClosure = sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherClosure { - return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherClosure(url, thumbnailUrl, videoInfo, caption, formattedCaption, progressWatcher) + if let sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure = sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure { + return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure(url, thumbnailUrl, videoInfo, caption, formattedCaption, storeInCache, progressWatcher) } else { - return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherReturnValue + return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReturnValue } } //MARK: - sendVoiceMessage - var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount = 0 - open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherCallsCount: Int { + var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = 0 + open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount: Int { get { if Thread.isMainThread { - return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount + return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount + returnValue = sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount } return returnValue! @@ -19325,29 +19250,29 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount = newValue + sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUnderlyingCallsCount = newValue + sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = newValue } } } } - open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherCalled: Bool { - return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherCallsCount > 0 + open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherCalled: Bool { + return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount > 0 } - open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherReceivedArguments: (url: String, audioInfo: AudioInfo, waveform: [UInt16], caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?)? - open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherReceivedInvocations: [(url: String, audioInfo: AudioInfo, waveform: [UInt16], caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?)] = [] + open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedArguments: (url: String, audioInfo: AudioInfo, waveform: [UInt16], caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?)? + open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedInvocations: [(url: String, audioInfo: AudioInfo, waveform: [UInt16], caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?)] = [] - var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue: SendAttachmentJoinHandle! - open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherReturnValue: SendAttachmentJoinHandle! { + var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue: SendAttachmentJoinHandle! + open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherReturnValue: SendAttachmentJoinHandle! { get { if Thread.isMainThread { - return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue + return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue } else { var returnValue: SendAttachmentJoinHandle? = nil DispatchQueue.main.sync { - returnValue = sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue + returnValue = sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue } return returnValue! @@ -19355,26 +19280,26 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue = newValue + sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUnderlyingReturnValue = newValue + sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue = newValue } } } } - open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherClosure: ((String, AudioInfo, [UInt16], String?, FormattedBody?, ProgressWatcher?) -> SendAttachmentJoinHandle)? + open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherClosure: ((String, AudioInfo, [UInt16], String?, FormattedBody?, Bool, ProgressWatcher?) -> SendAttachmentJoinHandle)? - open override func sendVoiceMessage(url: String, audioInfo: AudioInfo, waveform: [UInt16], caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?) -> SendAttachmentJoinHandle { - sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherCallsCount += 1 - sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherReceivedArguments = (url: url, audioInfo: audioInfo, waveform: waveform, caption: caption, formattedCaption: formattedCaption, progressWatcher: progressWatcher) + open override func sendVoiceMessage(url: String, audioInfo: AudioInfo, waveform: [UInt16], caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?) -> SendAttachmentJoinHandle { + sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount += 1 + sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedArguments = (url: url, audioInfo: audioInfo, waveform: waveform, caption: caption, formattedCaption: formattedCaption, storeInCache: storeInCache, progressWatcher: progressWatcher) DispatchQueue.main.async { - self.sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherReceivedInvocations.append((url: url, audioInfo: audioInfo, waveform: waveform, caption: caption, formattedCaption: formattedCaption, progressWatcher: progressWatcher)) + self.sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedInvocations.append((url: url, audioInfo: audioInfo, waveform: waveform, caption: caption, formattedCaption: formattedCaption, storeInCache: storeInCache, progressWatcher: progressWatcher)) } - if let sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherClosure = sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherClosure { - return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherClosure(url, audioInfo, waveform, caption, formattedCaption, progressWatcher) + if let sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherClosure = sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherClosure { + return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherClosure(url, audioInfo, waveform, caption, formattedCaption, storeInCache, progressWatcher) } else { - return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherReturnValue + return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherReturnValue } } diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 1551008861..be954997a9 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -417,7 +417,6 @@ class ClientProxy: ClientProxyProtocol { func knockRoom(_ roomID: String, message: String?) async -> Result { do { - // TODO: It should also include a message but the API for is not available yet let _ = try await client.knock(roomIdOrAlias: roomID) await waitForRoomToSync(roomID: roomID, timeout: .seconds(30)) return .success(()) @@ -429,7 +428,6 @@ class ClientProxy: ClientProxyProtocol { func knockRoomAlias(_ roomAlias: String, message: String?) async -> Result { do { - // TODO: It should also include a message but the API for is not available yet let room = try await client.knock(roomIdOrAlias: roomAlias) await waitForRoomToSync(roomID: room.id(), timeout: .seconds(30)) return .success(()) diff --git a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift index 876cfcee3a..d524dbb435 100644 --- a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift +++ b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift @@ -39,16 +39,7 @@ enum ClientProxyError: Error { } enum SlidingSyncConstants { - static let defaultTimelineLimit: UInt32 = 20 static let maximumVisibleRangeSize = 30 - static let defaultRequiredState = [ - RequiredState(key: "m.room.name", value: ""), - RequiredState(key: "m.room.topic", value: ""), - RequiredState(key: "m.room.avatar", value: ""), - RequiredState(key: "m.room.canonical_alias", value: ""), - RequiredState(key: "m.room.join_rules", value: ""), - RequiredState(key: "m.room.pinned_events", value: "") - ] } /// This struct represents the configuration that we are using to register the application through Pusher to Sygnal diff --git a/ElementX/Sources/Services/Room/JoinedRoomProxy.swift b/ElementX/Sources/Services/Room/JoinedRoomProxy.swift index 13d3f75921..4ada990bcf 100644 --- a/ElementX/Sources/Services/Room/JoinedRoomProxy.swift +++ b/ElementX/Sources/Services/Room/JoinedRoomProxy.swift @@ -187,11 +187,9 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol { } subscribedForUpdates = true - let settings = RoomSubscription(requiredState: SlidingSyncConstants.defaultRequiredState, - timelineLimit: SlidingSyncConstants.defaultTimelineLimit, - includeHeroes: false) // We don't need heroes here as they're already included in the `all_rooms` list + do { - try roomListService.subscribeToRooms(roomIds: [id], settings: settings) + try roomListService.subscribeToRooms(roomIds: [id]) } catch { MXLog.error("Failed subscribing to room with error: \(error)") } diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift index b1b4c37192..db51b77c8e 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift @@ -176,10 +176,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { guard let self else { return } do { - try roomListService.subscribeToRooms(roomIds: roomIDs, - settings: .init(requiredState: SlidingSyncConstants.defaultRequiredState, - timelineLimit: SlidingSyncConstants.defaultTimelineLimit, - includeHeroes: false)) + try roomListService.subscribeToRooms(roomIds: roomIDs) } catch { MXLog.error("Failed subscribing to rooms with error: \(error)") } diff --git a/ElementX/Sources/Services/Timeline/TimelineProxy.swift b/ElementX/Sources/Services/Timeline/TimelineProxy.swift index 659a12d1ea..2453bada76 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxy.swift @@ -227,9 +227,14 @@ final class TimelineProxy: TimelineProxyProtocol { requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result { MXLog.info("Sending audio") - let handle = timeline.sendAudio(url: url.path(percentEncoded: false), audioInfo: audioInfo, caption: nil, formattedCaption: nil, progressWatcher: UploadProgressListener { progress in - progressSubject?.send(progress) - }) + let handle = timeline.sendAudio(url: url.path(percentEncoded: false), + audioInfo: audioInfo, + caption: nil, + formattedCaption: nil, + storeInCache: true, + progressWatcher: UploadProgressListener { progress in + progressSubject?.send(progress) + }) await requestHandle(handle) @@ -250,9 +255,12 @@ final class TimelineProxy: TimelineProxyProtocol { requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result { MXLog.info("Sending file") - let handle = timeline.sendFile(url: url.path(percentEncoded: false), fileInfo: fileInfo, progressWatcher: UploadProgressListener { progress in - progressSubject?.send(progress) - }) + let handle = timeline.sendFile(url: url.path(percentEncoded: false), + fileInfo: fileInfo, + storeInCache: true, + progressWatcher: UploadProgressListener { progress in + progressSubject?.send(progress) + }) await requestHandle(handle) @@ -274,9 +282,15 @@ final class TimelineProxy: TimelineProxyProtocol { requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result { MXLog.info("Sending image") - let handle = timeline.sendImage(url: url.path(percentEncoded: false), thumbnailUrl: thumbnailURL.path(percentEncoded: false), imageInfo: imageInfo, caption: nil, formattedCaption: nil, progressWatcher: UploadProgressListener { progress in - progressSubject?.send(progress) - }) + let handle = timeline.sendImage(url: url.path(percentEncoded: false), + thumbnailUrl: thumbnailURL.path(percentEncoded: false), + imageInfo: imageInfo, + caption: nil, + formattedCaption: nil, + storeInCache: true, + progressWatcher: UploadProgressListener { progress in + progressSubject?.send(progress) + }) await requestHandle(handle) @@ -316,9 +330,15 @@ final class TimelineProxy: TimelineProxyProtocol { requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result { MXLog.info("Sending video") - let handle = timeline.sendVideo(url: url.path(percentEncoded: false), thumbnailUrl: thumbnailURL.path(percentEncoded: false), videoInfo: videoInfo, caption: nil, formattedCaption: nil, progressWatcher: UploadProgressListener { progress in - progressSubject?.send(progress) - }) + let handle = timeline.sendVideo(url: url.path(percentEncoded: false), + thumbnailUrl: thumbnailURL.path(percentEncoded: false), + videoInfo: videoInfo, + caption: nil, + formattedCaption: nil, + storeInCache: true, + progressWatcher: UploadProgressListener { progress in + progressSubject?.send(progress) + }) await requestHandle(handle) @@ -340,9 +360,15 @@ final class TimelineProxy: TimelineProxyProtocol { requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result { MXLog.info("Sending voice message") - let handle = timeline.sendVoiceMessage(url: url.path(percentEncoded: false), audioInfo: audioInfo, waveform: waveform, caption: nil, formattedCaption: nil, progressWatcher: UploadProgressListener { progress in - progressSubject?.send(progress) - }) + let handle = timeline.sendVoiceMessage(url: url.path(percentEncoded: false), + audioInfo: audioInfo, + waveform: waveform, + caption: nil, + formattedCaption: nil, + storeInCache: true, + progressWatcher: UploadProgressListener { progress in + progressSubject?.send(progress) + }) await requestHandle(handle) diff --git a/project.yml b/project.yml index adbf58d8a6..15f455f9f2 100644 --- a/project.yml +++ b/project.yml @@ -60,7 +60,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/element-hq/matrix-rust-components-swift - exactVersion: 1.0.59 + exactVersion: 1.0.60 # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios From 01886bb4d805bdaea60af945f3e9aa6436934098 Mon Sep 17 00:00:00 2001 From: Element CI Date: Wed, 23 Oct 2024 02:48:58 -0700 Subject: [PATCH 072/114] Prepare next release --- CHANGES.md | 33 ++++++++++++++++++++++++++++++ ElementX.xcodeproj/project.pbxproj | 4 ++-- project.yml | 2 +- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7dbb999810..d7642718e8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,36 @@ +## Changes in 1.9.2 (2024-10-23) + +### What's Changed + +🙌 Improvements +* Add support for rendering media captions in the timeline. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3429 +* Show a verification badge on the Room Member/User Profile screens. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3427 + +🐛 Bugfixes +* Only subscribe to identity updates if the room is encrypted. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3414 +* Fix the pinned identity banner to always show the user ID regardless of ambiguity. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3415 +* Fix a bug where uploaded images could have the wrong aspect ratio in the timeline. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3435 + +⚠️ API Changes +* Adopt various rust side Timeline API additions by @stefanceriu in https://github.com/element-hq/element-x-ios/pull/3423 + +🗣 Translations +* Translations update by @RiotRobot in https://github.com/element-hq/element-x-ios/pull/3433 + +🚧 In development 🚧 +* Allow image uploads to be optimised to reduce bandwidth. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3412 +* Knock and knocked state for the join room screen by @Velin92 in https://github.com/element-hq/element-x-ios/pull/3424 + +Others +* Fix some warnings. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3416 +* Refactor the`TimelineItemIdentifier` handling by @stefanceriu in https://github.com/element-hq/element-x-ios/pull/3418 +* Remove superfluous media request upload handle cancellation call. by @stefanceriu in https://github.com/element-hq/element-x-ios/pull/3425 +* Update dependency fastlane to v2.225.0 by @renovate in https://github.com/element-hq/element-x-ios/pull/3434 +* Adopt various Rust side API changes by @stefanceriu in https://github.com/element-hq/element-x-ios/pull/3437 + + +**Full Changelog**: https://github.com/element-hq/element-x-ios/compare/1.9.1...1.9.2 + ## Changes in 1.9.1 (2024-10-15) ### What's Changed diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index a3de5cd3b3..b0a62cc480 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7461,7 +7461,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCTION_APP_NAME = Element; @@ -7538,7 +7538,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.9.2; + MARKETING_VERSION = 1.9.3; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; diff --git a/project.yml b/project.yml index 15f455f9f2..9614142e27 100644 --- a/project.yml +++ b/project.yml @@ -41,7 +41,7 @@ settings: APP_GROUP_IDENTIFIER: group.$(BASE_APP_GROUP_IDENTIFIER) APP_NAME: ElementX KEYCHAIN_ACCESS_GROUP_IDENTIFIER: "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)" - MARKETING_VERSION: 1.9.2 + MARKETING_VERSION: 1.9.3 CURRENT_PROJECT_VERSION: 1 SUPPORTS_MACCATALYST: false From 6584b4d084525b9184f747ee98133974a1f59c21 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Wed, 23 Oct 2024 12:13:14 +0100 Subject: [PATCH 073/114] Update HeroImage to match the BigIcon component from Compound. (#3439) We can move it across in a following PR. --- ElementX.xcodeproj/project.pbxproj | 8 +- .../Views/{HeroImage.swift => BigIcon.swift} | 83 ++++++++++--------- .../View/AppLockSetupPINScreen.swift | 2 +- .../LoginScreen/View/LoginScreen.swift | 2 +- .../View/ServerConfirmationScreen.swift | 2 +- .../View/ServerSelectionScreen.swift | 2 +- .../View/EncryptionResetPasswordScreen.swift | 2 +- .../View/EncryptionResetScreen.swift | 2 +- .../JoinRoomScreen/View/JoinRoomScreen.swift | 2 +- .../View/AnalyticsPromptScreen.swift | 2 +- .../View/IdentityConfirmationScreen.swift | 2 +- .../View/IdentityConfirmedScreen.swift | 2 +- .../View/NotificationPermissionsScreen.swift | 2 +- .../View/SessionVerificationScreen.swift | 4 +- .../View/PinnedEventsTimelineScreen.swift | 2 +- .../View/QRCodeLoginScreen.swift | 14 ++-- ...ResolveVerifiedUserSendFailureScreen.swift | 2 +- .../View/SecureBackupKeyBackupScreen.swift | 2 +- ...SecureBackupLogoutConfirmationScreen.swift | 2 +- .../View/SecureBackupRecoveryKeyScreen.swift | 2 +- .../Sources/GeneratedPreviewTests.swift | 12 +-- ...est_analyticsPromptScreen-iPad-en-GB.1.png | 4 +- ...st_analyticsPromptScreen-iPad-pseudo.1.png | 4 +- ...nalyticsPromptScreen-iPhone-16-en-GB.1.png | 4 +- ...alyticsPromptScreen-iPhone-16-pseudo.1.png | 4 +- ...pLockSetupPINScreen-iPad-en-GB.Confirm.png | 4 +- ...ppLockSetupPINScreen-iPad-en-GB.Create.png | 4 +- ...etupPINScreen-iPad-en-GB.Unlock-Failed.png | 4 +- ...ppLockSetupPINScreen-iPad-en-GB.Unlock.png | 4 +- ...LockSetupPINScreen-iPad-pseudo.Confirm.png | 4 +- ...pLockSetupPINScreen-iPad-pseudo.Create.png | 4 +- ...tupPINScreen-iPad-pseudo.Unlock-Failed.png | 4 +- ...pLockSetupPINScreen-iPad-pseudo.Unlock.png | 4 +- ...SetupPINScreen-iPhone-16-en-GB.Confirm.png | 4 +- ...kSetupPINScreen-iPhone-16-en-GB.Create.png | 4 +- ...INScreen-iPhone-16-en-GB.Unlock-Failed.png | 4 +- ...kSetupPINScreen-iPhone-16-en-GB.Unlock.png | 4 +- ...etupPINScreen-iPhone-16-pseudo.Confirm.png | 4 +- ...SetupPINScreen-iPhone-16-pseudo.Create.png | 4 +- ...NScreen-iPhone-16-pseudo.Unlock-Failed.png | 4 +- ...SetupPINScreen-iPhone-16-pseudo.Unlock.png | 4 +- .../test_bigIcon-iPad-en-GB.1.png | 3 + .../test_bigIcon-iPad-pseudo.1.png | 3 + .../test_bigIcon-iPhone-16-en-GB.1.png | 3 + .../test_bigIcon-iPhone-16-pseudo.1.png | 3 + ...yptionResetPasswordScreen-iPad-en-GB.1.png | 4 +- ...ptionResetPasswordScreen-iPad-pseudo.1.png | 4 +- ...nResetPasswordScreen-iPhone-16-en-GB.1.png | 4 +- ...ResetPasswordScreen-iPhone-16-pseudo.1.png | 4 +- ...est_encryptionResetScreen-iPad-en-GB.1.png | 4 +- ...st_encryptionResetScreen-iPad-pseudo.1.png | 4 +- ...ncryptionResetScreen-iPhone-16-en-GB.1.png | 4 +- ...cryptionResetScreen-iPhone-16-pseudo.1.png | 4 +- .../test_heroImage-iPad-en-GB.1.png | 3 - .../test_heroImage-iPad-pseudo.1.png | 3 - .../test_heroImage-iPhone-16-en-GB.1.png | 3 - .../test_heroImage-iPhone-16-pseudo.1.png | 3 - ...dentityConfirmationScreen-iPad-en-GB.1.png | 4 +- ...entityConfirmationScreen-iPad-pseudo.1.png | 4 +- ...tyConfirmationScreen-iPhone-16-en-GB.1.png | 4 +- ...yConfirmationScreen-iPhone-16-pseudo.1.png | 4 +- ...t_identityConfirmedScreen-iPad-en-GB.1.png | 4 +- ..._identityConfirmedScreen-iPad-pseudo.1.png | 4 +- ...ntityConfirmedScreen-iPhone-16-en-GB.1.png | 4 +- ...tityConfirmedScreen-iPhone-16-pseudo.1.png | 4 +- ...test_joinRoomScreen-iPad-en-GB.Knocked.png | 4 +- ...est_joinRoomScreen-iPad-pseudo.Knocked.png | 4 +- ...joinRoomScreen-iPhone-16-en-GB.Knocked.png | 4 +- ...oinRoomScreen-iPhone-16-pseudo.Knocked.png | 4 +- ...nScreen-iPad-en-GB.Credentials-Entered.png | 4 +- ...est_loginScreen-iPad-en-GB.Unsupported.png | 4 +- ...test_loginScreen-iPad-en-GB.matrix-org.png | 4 +- ...Screen-iPad-pseudo.Credentials-Entered.png | 4 +- ...st_loginScreen-iPad-pseudo.Unsupported.png | 4 +- ...est_loginScreen-iPad-pseudo.matrix-org.png | 4 +- ...en-iPhone-16-en-GB.Credentials-Entered.png | 4 +- ...oginScreen-iPhone-16-en-GB.Unsupported.png | 4 +- ...loginScreen-iPhone-16-en-GB.matrix-org.png | 4 +- ...n-iPhone-16-pseudo.Credentials-Entered.png | 4 +- ...ginScreen-iPhone-16-pseudo.Unsupported.png | 4 +- ...oginScreen-iPhone-16-pseudo.matrix-org.png | 4 +- ...ficationPermissionsScreen-iPad-en-GB.1.png | 4 +- ...icationPermissionsScreen-iPad-pseudo.1.png | 4 +- ...ionPermissionsScreen-iPhone-16-en-GB.1.png | 4 +- ...onPermissionsScreen-iPhone-16-pseudo.1.png | 4 +- ...dEventsTimelineScreen-iPad-en-GB.Empty.png | 4 +- ...EventsTimelineScreen-iPad-pseudo.Empty.png | 4 +- ...tsTimelineScreen-iPhone-16-en-GB.Empty.png | 4 +- ...sTimelineScreen-iPhone-16-pseudo.Empty.png | 4 +- ...qRCodeLoginScreen-iPad-en-GB.Cancelled.png | 4 +- ...RCodeLoginScreen-iPad-en-GB.Connecting.png | 4 +- ...creen-iPad-en-GB.Connection-not-secure.png | 4 +- ..._qRCodeLoginScreen-iPad-en-GB.Declined.png | 4 +- ...CodeLoginScreen-iPad-en-GB.Device-code.png | 4 +- ...Screen-iPad-en-GB.Device-not-signed-in.png | 4 +- ...Screen-iPad-en-GB.Device-not-supported.png | 4 +- ...t_qRCodeLoginScreen-iPad-en-GB.Expired.png | 4 +- ...t_qRCodeLoginScreen-iPad-en-GB.Initial.png | 4 +- ...t_qRCodeLoginScreen-iPad-en-GB.Invalid.png | 4 +- ...nScreen-iPad-en-GB.Linking-unsupported.png | 4 +- ...Screen-iPad-en-GB.No-Camera-Permission.png | 4 +- ..._qRCodeLoginScreen-iPad-en-GB.Scanning.png | 4 +- ...deLoginScreen-iPad-en-GB.Unknown-error.png | 4 +- ...ginScreen-iPad-en-GB.Verification-code.png | 4 +- ...RCodeLoginScreen-iPad-pseudo.Cancelled.png | 4 +- ...CodeLoginScreen-iPad-pseudo.Connecting.png | 4 +- ...reen-iPad-pseudo.Connection-not-secure.png | 4 +- ...qRCodeLoginScreen-iPad-pseudo.Declined.png | 4 +- ...odeLoginScreen-iPad-pseudo.Device-code.png | 4 +- ...creen-iPad-pseudo.Device-not-signed-in.png | 4 +- ...creen-iPad-pseudo.Device-not-supported.png | 4 +- ..._qRCodeLoginScreen-iPad-pseudo.Expired.png | 4 +- ..._qRCodeLoginScreen-iPad-pseudo.Initial.png | 4 +- ..._qRCodeLoginScreen-iPad-pseudo.Invalid.png | 4 +- ...Screen-iPad-pseudo.Linking-unsupported.png | 4 +- ...creen-iPad-pseudo.No-Camera-Permission.png | 4 +- ...qRCodeLoginScreen-iPad-pseudo.Scanning.png | 4 +- ...eLoginScreen-iPad-pseudo.Unknown-error.png | 4 +- ...inScreen-iPad-pseudo.Verification-code.png | 4 +- ...eLoginScreen-iPhone-16-en-GB.Cancelled.png | 4 +- ...LoginScreen-iPhone-16-en-GB.Connecting.png | 4 +- ...-iPhone-16-en-GB.Connection-not-secure.png | 4 +- ...deLoginScreen-iPhone-16-en-GB.Declined.png | 4 +- ...oginScreen-iPhone-16-en-GB.Device-code.png | 4 +- ...n-iPhone-16-en-GB.Device-not-signed-in.png | 4 +- ...n-iPhone-16-en-GB.Device-not-supported.png | 4 +- ...odeLoginScreen-iPhone-16-en-GB.Expired.png | 4 +- ...odeLoginScreen-iPhone-16-en-GB.Initial.png | 4 +- ...odeLoginScreen-iPhone-16-en-GB.Invalid.png | 4 +- ...en-iPhone-16-en-GB.Linking-unsupported.png | 4 +- ...n-iPhone-16-en-GB.No-Camera-Permission.png | 4 +- ...deLoginScreen-iPhone-16-en-GB.Scanning.png | 4 +- ...inScreen-iPhone-16-en-GB.Unknown-error.png | 4 +- ...reen-iPhone-16-en-GB.Verification-code.png | 4 +- ...LoginScreen-iPhone-16-pseudo.Cancelled.png | 4 +- ...oginScreen-iPhone-16-pseudo.Connecting.png | 4 +- ...iPhone-16-pseudo.Connection-not-secure.png | 4 +- ...eLoginScreen-iPhone-16-pseudo.Declined.png | 4 +- ...ginScreen-iPhone-16-pseudo.Device-code.png | 4 +- ...-iPhone-16-pseudo.Device-not-signed-in.png | 4 +- ...-iPhone-16-pseudo.Device-not-supported.png | 4 +- ...deLoginScreen-iPhone-16-pseudo.Expired.png | 4 +- ...deLoginScreen-iPhone-16-pseudo.Initial.png | 4 +- ...deLoginScreen-iPhone-16-pseudo.Invalid.png | 4 +- ...n-iPhone-16-pseudo.Linking-unsupported.png | 4 +- ...-iPhone-16-pseudo.No-Camera-Permission.png | 4 +- ...eLoginScreen-iPhone-16-pseudo.Scanning.png | 4 +- ...nScreen-iPhone-16-pseudo.Unknown-error.png | 4 +- ...een-iPhone-16-pseudo.Verification-code.png | 4 +- ...lureScreen-iPad-en-GB.Identity-Changed.png | 4 +- ...eScreen-iPad-en-GB.Own-Unsigned-Device.png | 4 +- ...ilureScreen-iPad-en-GB.Unsigned-Device.png | 4 +- ...ureScreen-iPad-pseudo.Identity-Changed.png | 4 +- ...Screen-iPad-pseudo.Own-Unsigned-Device.png | 4 +- ...lureScreen-iPad-pseudo.Unsigned-Device.png | 4 +- ...creen-iPhone-16-en-GB.Identity-Changed.png | 4 +- ...en-iPhone-16-en-GB.Own-Unsigned-Device.png | 4 +- ...Screen-iPhone-16-en-GB.Unsigned-Device.png | 4 +- ...reen-iPhone-16-pseudo.Identity-Changed.png | 4 +- ...n-iPhone-16-pseudo.Own-Unsigned-Device.png | 4 +- ...creen-iPhone-16-pseudo.Unsigned-Device.png | 4 +- ...ackupKeyBackupScreen-iPad-en-GB.Set-up.png | 4 +- ...ckupKeyBackupScreen-iPad-pseudo.Set-up.png | 4 +- ...KeyBackupScreen-iPhone-16-en-GB.Set-up.png | 4 +- ...eyBackupScreen-iPhone-16-pseudo.Set-up.png | 4 +- ...pLogoutConfirmationScreen-iPad-en-GB.1.png | 4 +- ...LogoutConfirmationScreen-iPad-pseudo.1.png | 4 +- ...utConfirmationScreen-iPhone-16-en-GB.1.png | 4 +- ...tConfirmationScreen-iPhone-16-pseudo.1.png | 4 +- ...ecoveryKeyScreen-iPad-en-GB.Incomplete.png | 4 +- ...ecoveryKeyScreen-iPad-en-GB.Not-set-up.png | 4 +- ...kupRecoveryKeyScreen-iPad-en-GB.Set-up.png | 4 +- ...upRecoveryKeyScreen-iPad-en-GB.Unknown.png | 4 +- ...coveryKeyScreen-iPad-pseudo.Incomplete.png | 4 +- ...coveryKeyScreen-iPad-pseudo.Not-set-up.png | 4 +- ...upRecoveryKeyScreen-iPad-pseudo.Set-up.png | 4 +- ...pRecoveryKeyScreen-iPad-pseudo.Unknown.png | 4 +- ...ryKeyScreen-iPhone-16-en-GB.Incomplete.png | 4 +- ...ryKeyScreen-iPhone-16-en-GB.Not-set-up.png | 4 +- ...coveryKeyScreen-iPhone-16-en-GB.Set-up.png | 4 +- ...overyKeyScreen-iPhone-16-en-GB.Unknown.png | 4 +- ...yKeyScreen-iPhone-16-pseudo.Incomplete.png | 4 +- ...yKeyScreen-iPhone-16-pseudo.Not-set-up.png | 4 +- ...overyKeyScreen-iPhone-16-pseudo.Set-up.png | 4 +- ...veryKeyScreen-iPhone-16-pseudo.Unknown.png | 4 +- ...verConfirmationScreen-iPad-en-GB.Login.png | 4 +- ...ConfirmationScreen-iPad-en-GB.Register.png | 4 +- ...erConfirmationScreen-iPad-pseudo.Login.png | 4 +- ...onfirmationScreen-iPad-pseudo.Register.png | 4 +- ...nfirmationScreen-iPhone-16-en-GB.Login.png | 4 +- ...rmationScreen-iPhone-16-en-GB.Register.png | 4 +- ...firmationScreen-iPhone-16-pseudo.Login.png | 4 +- ...mationScreen-iPhone-16-pseudo.Register.png | 4 +- .../test_serverSelection-iPad-en-GB.1.png | 4 +- .../test_serverSelection-iPad-en-GB.2.png | 4 +- .../test_serverSelection-iPad-en-GB.3.png | 4 +- .../test_serverSelection-iPad-pseudo.1.png | 4 +- .../test_serverSelection-iPad-pseudo.2.png | 4 +- .../test_serverSelection-iPad-pseudo.3.png | 4 +- ...test_serverSelection-iPhone-16-en-GB.1.png | 4 +- ...test_serverSelection-iPhone-16-en-GB.2.png | 4 +- ...test_serverSelection-iPhone-16-en-GB.3.png | 4 +- ...est_serverSelection-iPhone-16-pseudo.1.png | 4 +- ...est_serverSelection-iPhone-16-pseudo.2.png | 4 +- ...est_serverSelection-iPhone-16-pseudo.3.png | 4 +- ...ssionVerification-iPad-en-GB.Cancelled.png | 4 +- ...sessionVerification-iPad-en-GB.Initial.png | 4 +- ...rification-iPad-en-GB.Request-Accepted.png | 4 +- ...ion-iPad-en-GB.Requesting-Verification.png | 4 +- ...ification-iPad-en-GB.Showing-Challenge.png | 4 +- ...essionVerification-iPad-en-GB.Verified.png | 4 +- ...sionVerification-iPad-pseudo.Cancelled.png | 4 +- ...essionVerification-iPad-pseudo.Initial.png | 4 +- ...ification-iPad-pseudo.Request-Accepted.png | 4 +- ...on-iPad-pseudo.Requesting-Verification.png | 4 +- ...fication-iPad-pseudo.Showing-Challenge.png | 4 +- ...ssionVerification-iPad-pseudo.Verified.png | 4 +- ...Verification-iPhone-16-en-GB.Cancelled.png | 4 +- ...onVerification-iPhone-16-en-GB.Initial.png | 4 +- ...ation-iPhone-16-en-GB.Request-Accepted.png | 4 +- ...Phone-16-en-GB.Requesting-Verification.png | 4 +- ...tion-iPhone-16-en-GB.Showing-Challenge.png | 4 +- ...nVerification-iPhone-16-en-GB.Verified.png | 4 +- ...erification-iPhone-16-pseudo.Cancelled.png | 4 +- ...nVerification-iPhone-16-pseudo.Initial.png | 4 +- ...tion-iPhone-16-pseudo.Request-Accepted.png | 4 +- ...hone-16-pseudo.Requesting-Verification.png | 4 +- ...ion-iPhone-16-pseudo.Showing-Challenge.png | 4 +- ...Verification-iPhone-16-pseudo.Verified.png | 4 +- ...upFlow-0-iPad-10th-generation-en-GB.UI.png | 4 +- .../appLockSetupFlow-0-iPhone-16-en-GB.UI.png | 4 +- ...upFlow-1-iPad-10th-generation-en-GB.UI.png | 4 +- .../appLockSetupFlow-1-iPhone-16-en-GB.UI.png | 4 +- ...upFlow-4-iPad-10th-generation-en-GB.UI.png | 4 +- ...upFlow-5-iPad-10th-generation-en-GB.UI.png | 4 +- ...datory-0-iPad-10th-generation-en-GB.UI.png | 4 +- ...etupFlowMandatory-0-iPhone-16-en-GB.UI.png | 4 +- ...datory-1-iPad-10th-generation-en-GB.UI.png | 4 +- ...etupFlowMandatory-1-iPhone-16-en-GB.UI.png | 4 +- ...LockSetupFlowUnlock-iPhone-16-en-GB.UI.png | 4 +- ...onFlow-1-iPad-10th-generation-en-GB.UI.png | 4 +- ...uthenticationFlow-1-iPhone-16-en-GB.UI.png | 4 +- ...tionFlow-iPad-10th-generation-en-GB.UI.png | 4 +- .../authenticationFlow-iPhone-16-en-GB.UI.png | 4 +- ...ection-0-iPad-10th-generation-en-GB.UI.png | 4 +- .../serverSelection-0-iPhone-16-en-GB.UI.png | 4 +- ...ection-1-iPad-10th-generation-en-GB.UI.png | 4 +- .../serverSelection-1-iPhone-16-en-GB.UI.png | 4 +- ...ection-2-iPad-10th-generation-en-GB.UI.png | 4 +- .../serverSelection-2-iPhone-16-en-GB.UI.png | 4 +- ...ssionVerification-0-iPhone-16-en-GB.UI.png | 4 +- ...ssionVerification-1-iPhone-16-en-GB.UI.png | 4 +- ...ssionVerification-2-iPhone-16-en-GB.UI.png | 4 +- ...cation-4-iPad-10th-generation-en-GB.UI.png | 4 +- ...ssionVerification-4-iPhone-16-en-GB.UI.png | 4 +- ...cation-5-iPad-10th-generation-en-GB.UI.png | 4 +- ...ssionVerification-5-iPhone-16-en-GB.UI.png | 4 +- ...ssionVerification-6-iPhone-16-en-GB.UI.png | 4 +- ...ssionVerification-7-iPhone-16-en-GB.UI.png | 4 +- 259 files changed, 552 insertions(+), 545 deletions(-) rename ElementX/Sources/Other/SwiftUI/Views/{HeroImage.swift => BigIcon.swift} (51%) create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_bigIcon-iPad-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_bigIcon-iPad-pseudo.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_bigIcon-iPhone-16-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_bigIcon-iPhone-16-pseudo.1.png delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_heroImage-iPad-en-GB.1.png delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_heroImage-iPad-pseudo.1.png delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_heroImage-iPhone-16-en-GB.1.png delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_heroImage-iPhone-16-pseudo.1.png diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index b0a62cc480..5d1487ca48 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -962,7 +962,6 @@ D43F0503EF2CBC55272538FE /* SDKGeneratedMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2F079B5DBD0D85FEA687AAE /* SDKGeneratedMocks.swift */; }; D46C33F8B61B55F0C8C2D15F /* LocationRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2AC540DE619B36832A5DB5 /* LocationRoomTimelineItem.swift */; }; D4CB979EB4FE26AAD9F9A72B /* UserProfileScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 604A69C081B935D6A38DE6D8 /* UserProfileScreenModels.swift */; }; - D4D5595C4A2A702CFF4E94FF /* HeroImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EC2F1622C5BBABED6012E12 /* HeroImage.swift */; }; D4D7CCECC6C0AAFC42E165BB /* NotificationPermissionsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE9BBB18FB27F09032AD8769 /* NotificationPermissionsScreenViewModel.swift */; }; D53B80EF02C1062E68659EDD /* ReportContentViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086C19086DD16E9B38E25954 /* ReportContentViewModelTests.swift */; }; D55AF9B5B55FEED04771A461 /* RoomFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A008E57D52B07B78DFAD1BB /* RoomFlowCoordinator.swift */; }; @@ -1132,6 +1131,7 @@ FB595EC9C00AB32F39034055 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A37E2FACFD041CE466223CD /* SceneDelegate.swift */; }; FBD402E3170EB1ED0D1AA672 /* EncryptionKeyProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2355398E4A55DA5A89128AD1 /* EncryptionKeyProvider.swift */; }; FBF09B6C900415800DDF2A21 /* EmojiProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C113E0CB7E15E9765B1817A /* EmojiProvider.swift */; }; + FC0EEFF630F34899953BB950 /* BigIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01FD1171FF40E34D707FD00 /* BigIcon.swift */; }; FC10228E73323BDC09526F97 /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = 4278261E147DB2DE5CFB7FC5 /* PostHog */; }; FC8B95EC506E6BB5793D81CE /* ClientProtocolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E34685D186453E429ADEE58E /* ClientProtocolTests.swift */; }; FCD3F2B82CAB29A07887A127 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 2B43F2AF7456567FE37270A7 /* KeychainAccess */; }; @@ -1751,7 +1751,6 @@ 7E492690C8B27A892C194CC4 /* AdvancedSettingsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenCoordinator.swift; sourceTree = ""; }; 7E8562F4D7DE073BC32902AB /* EncryptionResetScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetScreenViewModelProtocol.swift; sourceTree = ""; }; 7EB58E4E8D6D634C246AD5C2 /* RoomInviterLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomInviterLabel.swift; sourceTree = ""; }; - 7EC2F1622C5BBABED6012E12 /* HeroImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeroImage.swift; sourceTree = ""; }; 7EECE8B331CD169790EF284F /* BugReportScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportScreenViewModelTests.swift; sourceTree = ""; }; 7F615A00DB223FF3280204D2 /* UserDiscoveryServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDiscoveryServiceProtocol.swift; sourceTree = ""; }; 7FB2253D36E81E045E1CB432 /* Duration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Duration.swift; sourceTree = ""; }; @@ -2114,6 +2113,7 @@ CDE3F3911FF7CC639BDE5844 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; CEE20623EB4A9B88FB29F2BA /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/SAS.strings; sourceTree = ""; }; CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; path = UnitTests.xctestplan; sourceTree = ""; }; + D01FD1171FF40E34D707FD00 /* BigIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BigIcon.swift; sourceTree = ""; }; D071F86CD47582B9196C9D16 /* UserDiscoverySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDiscoverySection.swift; sourceTree = ""; }; D086854995173E897F993C26 /* AdvancedSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenViewModelProtocol.swift; sourceTree = ""; }; D09A267106B9585D3D0CFC0D /* ClientError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientError.swift; sourceTree = ""; }; @@ -2955,8 +2955,8 @@ CC743C7A85E3171BCBF0A653 /* AvatarHeaderView.swift */, 9A028783CFFF861C5E44FFB1 /* BadgeLabel.swift */, C1FA515B3B0D61EF1E907D2D /* BadgeView.swift */, + D01FD1171FF40E34D707FD00 /* BigIcon.swift */, 8CC23C63849452BC86EA2852 /* ButtonStyle.swift */, - 7EC2F1622C5BBABED6012E12 /* HeroImage.swift */, B590BD4507D4F0A377FDE01A /* LoadableAvatarImage.swift */, C352359663A0E52BA20761EE /* LoadableImage.swift */, FFECCE59967018204876D0A5 /* LocationMarkerView.swift */, @@ -6343,6 +6343,7 @@ D876EC0FED3B6D46C806912A /* AvatarSize.swift in Sources */, 7A25D6926A2C01DB8D0D67A5 /* BadgeLabel.swift in Sources */, A4B0BAD62A12ED76BD611B79 /* BadgeView.swift in Sources */, + FC0EEFF630F34899953BB950 /* BigIcon.swift in Sources */, 38546A6010A2CF240EC9AF73 /* BindableState.swift in Sources */, EB9F4688006B52E69DF5358F /* BlankFormCoordinator.swift in Sources */, 369BF960E52BBEE61F8A5BD1 /* BlockedUsersScreen.swift in Sources */, @@ -6494,7 +6495,6 @@ D34E328E9E65904358248FDD /* GlobalSearchScreenModels.swift in Sources */, 55D18AA4F4A2257642EBDB94 /* GlobalSearchScreenViewModel.swift in Sources */, E32A18802EB37EEE3EF7B965 /* GlobalSearchScreenViewModelProtocol.swift in Sources */, - D4D5595C4A2A702CFF4E94FF /* HeroImage.swift in Sources */, 0C1E537A49ABB386F7554D4A /* HighlightedTimelineItemModifier.swift in Sources */, 964B9D2EC38C488C360CE0C9 /* HomeScreen.swift in Sources */, 62C5876C4254C58C2086F0DE /* HomeScreenContent.swift in Sources */, diff --git a/ElementX/Sources/Other/SwiftUI/Views/HeroImage.swift b/ElementX/Sources/Other/SwiftUI/Views/BigIcon.swift similarity index 51% rename from ElementX/Sources/Other/SwiftUI/Views/HeroImage.swift rename to ElementX/Sources/Other/SwiftUI/Views/BigIcon.swift index f50ecb11e3..2551c02ca1 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/HeroImage.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/BigIcon.swift @@ -10,39 +10,36 @@ import SwiftUI /// An image that is styled for use as the main/top/hero screen icon. This component /// takes a compound icon. If you would like to apply it to an SFSymbol, you can call -/// the `heroImage()` modifier directly on the Image. -struct HeroImage: View { +/// the `bigIcon()` modifier directly on the Image. +struct BigIcon: View { enum Style { - case normal - case subtle + case defaultSolid + case `default` + case alertSolid + case alert + case successSolid case success - case critical - case criticalOnSecondary var foregroundColor: Color { switch self { - case .normal: - .compound.iconPrimary - case .subtle: + case .defaultSolid, .default: .compound.iconSecondary - case .success: - .compound.iconSuccessPrimary - case .critical, .criticalOnSecondary: + case .alertSolid, .alert: .compound.iconCriticalPrimary + case .successSolid, .success: + .compound.iconSuccessPrimary } } var backgroundFillColor: Color { switch self { - case .normal: + case .defaultSolid: .compound.bgSubtleSecondary - case .subtle: - .compound.bgSubtlePrimary - case .success: - .compound.bgSuccessSubtle - case .critical: + case .alertSolid: .compound.bgCriticalSubtle - case .criticalOnSecondary: + case .successSolid: + .compound.bgSuccessSubtle + case .default, .alert, .success: .compound.bgCanvasDefault } } @@ -50,32 +47,32 @@ struct HeroImage: View { /// The icon that is shown. let icon: KeyPath - var style: Style = .normal + var style: Style = .defaultSolid var body: some View { - CompoundIcon(icon, size: .custom(42), relativeTo: .title) - .modifier(HeroImageModifier(style: style)) + CompoundIcon(icon, size: .custom(32), relativeTo: .title) + .modifier(BigIconModifier(style: style)) } } extension Image { /// Styles the image for use as the main/top/hero screen icon. You should prefer - /// the HeroImage component when possible, by using an icon from Compound. - func heroImage(insets: CGFloat = 16, style: HeroImage.Style = .normal) -> some View { + /// the BigIcon component when possible, by using an icon from Compound. + func bigIcon(insets: CGFloat = 16, style: BigIcon.Style = .defaultSolid) -> some View { resizable() .renderingMode(.template) .aspectRatio(contentMode: .fit) .scaledPadding(insets, relativeTo: .title) - .modifier(HeroImageModifier(style: style)) + .modifier(BigIconModifier(style: style)) } } -private struct HeroImageModifier: ViewModifier { - let style: HeroImage.Style +private struct BigIconModifier: ViewModifier { + let style: BigIcon.Style func body(content: Content) -> some View { content - .scaledFrame(size: 70, relativeTo: .title) + .scaledFrame(size: 64, relativeTo: .title) .foregroundColor(style.foregroundColor) .background { RoundedRectangle(cornerRadius: 14) @@ -87,22 +84,32 @@ private struct HeroImageModifier: ViewModifier { // MARK: - Previews -struct HeroImage_Previews: PreviewProvider, TestablePreview { +struct BigIcon_Previews: PreviewProvider, TestablePreview { static var previews: some View { - VStack(spacing: 20) { + VStack(spacing: 40) { HStack(spacing: 20) { - HeroImage(icon: \.lockSolid) + BigIcon(icon: \.lockSolid) Image(systemName: "hourglass") - .heroImage() + .bigIcon() Image(asset: Asset.Images.serverSelectionIcon) - .heroImage(insets: 19) + .bigIcon(insets: 19) } - HStack(spacing: 20) { - HeroImage(icon: \.helpSolid, style: .subtle) - HeroImage(icon: \.checkCircleSolid, style: .success) - HeroImage(icon: \.error, style: .critical) - HeroImage(icon: \.error, style: .criticalOnSecondary) + VStack(spacing: 20) { + HStack(spacing: 20) { + BigIcon(icon: \.helpSolid) + BigIcon(icon: \.helpSolid, style: .default) + } + + HStack(spacing: 20) { + BigIcon(icon: \.error, style: .alertSolid) + BigIcon(icon: \.error, style: .alert) + } + + HStack(spacing: 20) { + BigIcon(icon: \.checkCircleSolid, style: .successSolid) + BigIcon(icon: \.checkCircleSolid, style: .success) + } } } } diff --git a/ElementX/Sources/Screens/AppLock/AppLockSetupPINScreen/View/AppLockSetupPINScreen.swift b/ElementX/Sources/Screens/AppLock/AppLockSetupPINScreen/View/AppLockSetupPINScreen.swift index 95a93f289b..d0ca7d03d8 100644 --- a/ElementX/Sources/Screens/AppLock/AppLockSetupPINScreen/View/AppLockSetupPINScreen.swift +++ b/ElementX/Sources/Screens/AppLock/AppLockSetupPINScreen/View/AppLockSetupPINScreen.swift @@ -58,7 +58,7 @@ struct AppLockSetupPINScreen: View { var header: some View { VStack(spacing: 8) { - HeroImage(icon: \.lockSolid) + BigIcon(icon: \.lockSolid) .padding(.bottom, 8) Text(context.viewState.title) diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginScreen.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginScreen.swift index 1610591e65..b5e882ee63 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginScreen.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginScreen.swift @@ -45,7 +45,7 @@ struct LoginScreen: View { /// The header containing the title and icon. var header: some View { VStack(spacing: 8) { - HeroImage(icon: \.lockSolid) + BigIcon(icon: \.lockSolid) .padding(.bottom, 8) Text(L10n.screenLoginTitleWithHomeserver(context.viewState.homeserver.address)) diff --git a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/View/ServerConfirmationScreen.swift b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/View/ServerConfirmationScreen.swift index 14beaed6f9..701e845f55 100644 --- a/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/View/ServerConfirmationScreen.swift +++ b/ElementX/Sources/Screens/Authentication/ServerConfirmationScreen/View/ServerConfirmationScreen.swift @@ -29,7 +29,7 @@ struct ServerConfirmationScreen: View { var header: some View { VStack(spacing: 8) { Image(systemSymbol: .personCropCircleFill) - .heroImage() + .bigIcon() .padding(.bottom, 8) Text(context.viewState.title) diff --git a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/View/ServerSelectionScreen.swift b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/View/ServerSelectionScreen.swift index 122b34a171..49e792fcb8 100644 --- a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/View/ServerSelectionScreen.swift +++ b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/View/ServerSelectionScreen.swift @@ -32,7 +32,7 @@ struct ServerSelectionScreen: View { var header: some View { VStack(spacing: 8) { Image(asset: Asset.Images.serverSelectionIcon) - .heroImage(insets: 19) + .bigIcon(insets: 19) .padding(.bottom, 8) Text(L10n.screenChangeServerTitle) diff --git a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/View/EncryptionResetPasswordScreen.swift b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/View/EncryptionResetPasswordScreen.swift index 20b934e1ce..03681a81d4 100644 --- a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/View/EncryptionResetPasswordScreen.swift +++ b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/View/EncryptionResetPasswordScreen.swift @@ -15,7 +15,7 @@ struct EncryptionResetPasswordScreen: View { var body: some View { FullscreenDialog { VStack(spacing: 16) { - HeroImage(icon: \.lockSolid) + BigIcon(icon: \.lockSolid) Text(L10n.screenResetEncryptionPasswordTitle) .foregroundColor(.compound.textPrimary) diff --git a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/View/EncryptionResetScreen.swift b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/View/EncryptionResetScreen.swift index 54f40c4bfc..bd8956493d 100644 --- a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/View/EncryptionResetScreen.swift +++ b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/View/EncryptionResetScreen.swift @@ -39,7 +39,7 @@ struct EncryptionResetScreen: View { private var header: some View { VStack(spacing: 8) { - HeroImage(icon: \.error, style: .criticalOnSecondary) + BigIcon(icon: \.error, style: .alert) .padding(.bottom, 8) Text(L10n.screenEncryptionResetTitle) diff --git a/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift b/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift index fb83492752..87035a6a78 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift @@ -88,7 +88,7 @@ struct JoinRoomScreen: View { @ViewBuilder private var knockedView: some View { VStack(spacing: 16) { - HeroImage(icon: \.checkCircleSolid, style: .success) + BigIcon(icon: \.checkCircleSolid, style: .successSolid) VStack(spacing: 8) { Text(L10n.screenJoinRoomKnockSentTitle) .font(.compound.headingMDBold) diff --git a/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/View/AnalyticsPromptScreen.swift b/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/View/AnalyticsPromptScreen.swift index c616acc34f..08a93f44d1 100644 --- a/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/View/AnalyticsPromptScreen.swift +++ b/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/View/AnalyticsPromptScreen.swift @@ -35,7 +35,7 @@ struct AnalyticsPromptScreen: View { private var header: some View { VStack(spacing: 8) { - HeroImage(icon: \.chart) + BigIcon(icon: \.chart) .padding(.bottom, 8) Text(L10n.screenAnalyticsPromptTitle(InfoPlistReader.main.bundleDisplayName)) diff --git a/ElementX/Sources/Screens/Onboarding/IdentityConfirmationScreen/View/IdentityConfirmationScreen.swift b/ElementX/Sources/Screens/Onboarding/IdentityConfirmationScreen/View/IdentityConfirmationScreen.swift index fc91466573..4bbb249ef3 100644 --- a/ElementX/Sources/Screens/Onboarding/IdentityConfirmationScreen/View/IdentityConfirmationScreen.swift +++ b/ElementX/Sources/Screens/Onboarding/IdentityConfirmationScreen/View/IdentityConfirmationScreen.swift @@ -37,7 +37,7 @@ struct IdentityConfirmationScreen: View { @ViewBuilder private var screenHeader: some View { VStack(spacing: 0) { - HeroImage(icon: \.lockSolid) + BigIcon(icon: \.lockSolid) .padding(.bottom, 16) Text(L10n.screenIdentityConfirmationTitle) diff --git a/ElementX/Sources/Screens/Onboarding/IdentityConfirmedScreen/View/IdentityConfirmedScreen.swift b/ElementX/Sources/Screens/Onboarding/IdentityConfirmedScreen/View/IdentityConfirmedScreen.swift index fc8b705727..aa7bd0a1de 100644 --- a/ElementX/Sources/Screens/Onboarding/IdentityConfirmedScreen/View/IdentityConfirmedScreen.swift +++ b/ElementX/Sources/Screens/Onboarding/IdentityConfirmedScreen/View/IdentityConfirmedScreen.swift @@ -29,7 +29,7 @@ struct IdentityConfirmedScreen: View { @ViewBuilder private var screenHeader: some View { VStack(spacing: 0) { - HeroImage(icon: \.checkCircle, style: .success) + BigIcon(icon: \.checkCircle, style: .successSolid) .padding(.bottom, 16) Text(L10n.screenIdentityConfirmedTitle) diff --git a/ElementX/Sources/Screens/Onboarding/NotificationPermissionsScreen/View/NotificationPermissionsScreen.swift b/ElementX/Sources/Screens/Onboarding/NotificationPermissionsScreen/View/NotificationPermissionsScreen.swift index 8d042091a2..d3b4872fec 100644 --- a/ElementX/Sources/Screens/Onboarding/NotificationPermissionsScreen/View/NotificationPermissionsScreen.swift +++ b/ElementX/Sources/Screens/Onboarding/NotificationPermissionsScreen/View/NotificationPermissionsScreen.swift @@ -27,7 +27,7 @@ struct NotificationPermissionsScreen: View { /// The main content of the screen that is shown inside the scroll view. private var mainContent: some View { VStack(spacing: 8) { - HeroImage(icon: \.notificationsSolid) + BigIcon(icon: \.notificationsSolid) .padding(.bottom, 8) Text(L10n.screenNotificationOptinTitle) diff --git a/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/View/SessionVerificationScreen.swift b/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/View/SessionVerificationScreen.swift index ac76937f55..553c34e67f 100644 --- a/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/View/SessionVerificationScreen.swift +++ b/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/View/SessionVerificationScreen.swift @@ -58,11 +58,11 @@ struct SessionVerificationScreen: View { private var screenHeader: some View { VStack(spacing: 0) { if context.viewState.verificationState == .initial { - HeroImage(icon: \.lockSolid) + BigIcon(icon: \.lockSolid) .padding(.bottom, 16) } else { Image(systemName: headerImageName) - .heroImage() + .bigIcon() .padding(.bottom, 16) } diff --git a/ElementX/Sources/Screens/PinnedEventsTimelineScreen/View/PinnedEventsTimelineScreen.swift b/ElementX/Sources/Screens/PinnedEventsTimelineScreen/View/PinnedEventsTimelineScreen.swift index 5e8b75eaa2..c144ae4666 100644 --- a/ElementX/Sources/Screens/PinnedEventsTimelineScreen/View/PinnedEventsTimelineScreen.swift +++ b/ElementX/Sources/Screens/PinnedEventsTimelineScreen/View/PinnedEventsTimelineScreen.swift @@ -50,7 +50,7 @@ struct PinnedEventsTimelineScreen: View { private var content: some View { if timelineContext.viewState.pinnedEventIDs.isEmpty { VStack(spacing: 16) { - HeroImage(icon: \.pin, style: .normal) + BigIcon(icon: \.pin) Text(L10n.screenPinnedTimelineEmptyStateHeadline) .font(.compound.headingSMSemibold) .foregroundStyle(.compound.textPrimary) diff --git a/ElementX/Sources/Screens/QRCodeLoginScreen/View/QRCodeLoginScreen.swift b/ElementX/Sources/Screens/QRCodeLoginScreen/View/QRCodeLoginScreen.swift index e0349cab90..13d9184fcb 100644 --- a/ElementX/Sources/Screens/QRCodeLoginScreen/View/QRCodeLoginScreen.swift +++ b/ElementX/Sources/Screens/QRCodeLoginScreen/View/QRCodeLoginScreen.swift @@ -41,7 +41,7 @@ struct QRCodeLoginScreen: View { FullscreenDialog { VStack(alignment: .leading, spacing: 40) { VStack(spacing: 16) { - HeroImage(icon: \.computer, style: .subtle) + BigIcon(icon: \.computer, style: .default) VStack(spacing: 8) { Text(L10n.screenQrCodeLoginInitialStateTitle(InfoPlistReader.main.productionAppName)) @@ -101,7 +101,7 @@ struct QRCodeLoginScreen: View { VStack(spacing: 16) { switch state { case .deviceCode: - HeroImage(icon: \.computer, style: .subtle) + BigIcon(icon: \.computer, style: .default) VStack(spacing: 8) { Text(L10n.screenQrCodeLoginDeviceCodeTitle) @@ -115,7 +115,7 @@ struct QRCodeLoginScreen: View { .multilineTextAlignment(.center) } case .verificationCode: - HeroImage(icon: \.lock, style: .subtle) + BigIcon(icon: \.lock, style: .default) VStack(spacing: 8) { Text(L10n.screenQrCodeLoginVerifyCodeTitle) @@ -136,7 +136,7 @@ struct QRCodeLoginScreen: View { FullscreenDialog { VStack(spacing: 40) { VStack(spacing: 16) { - HeroImage(icon: \.takePhotoSolid, style: .subtle) + BigIcon(icon: \.takePhotoSolid, style: .default) Text(L10n.screenQrCodeLoginScanningStateTitle) .foregroundColor(.compound.textPrimary) @@ -256,7 +256,7 @@ struct QRCodeLoginScreen: View { switch errorState { case .noCameraPermission: VStack(spacing: 16) { - HeroImage(icon: \.takePhotoSolid, style: .subtle) + BigIcon(icon: \.takePhotoSolid, style: .default) VStack(spacing: 8) { Text(L10n.screenQrCodeLoginNoCameraPermissionStateTitle) @@ -273,7 +273,7 @@ struct QRCodeLoginScreen: View { case .connectionNotSecure: VStack(spacing: 40) { VStack(spacing: 16) { - HeroImage(icon: \.error, style: .criticalOnSecondary) + BigIcon(icon: \.error, style: .alert) VStack(spacing: 8) { Text(L10n.screenQrCodeLoginConnectionNoteSecureStateTitle) @@ -339,7 +339,7 @@ struct QRCodeLoginScreen: View { } VStack(spacing: 16) { - HeroImage(icon: \.error, style: .criticalOnSecondary) + BigIcon(icon: \.error, style: .alert) VStack(spacing: 8) { Text(title) diff --git a/ElementX/Sources/Screens/ResolveVerifiedUserSendFailureScreen/View/ResolveVerifiedUserSendFailureScreen.swift b/ElementX/Sources/Screens/ResolveVerifiedUserSendFailureScreen/View/ResolveVerifiedUserSendFailureScreen.swift index 2964e317ba..b0ef698b15 100644 --- a/ElementX/Sources/Screens/ResolveVerifiedUserSendFailureScreen/View/ResolveVerifiedUserSendFailureScreen.swift +++ b/ElementX/Sources/Screens/ResolveVerifiedUserSendFailureScreen/View/ResolveVerifiedUserSendFailureScreen.swift @@ -38,7 +38,7 @@ struct ResolveVerifiedUserSendFailureScreen: View { var header: some View { VStack(spacing: 8) { - HeroImage(icon: \.error, style: .critical) + BigIcon(icon: \.error, style: .alertSolid) .padding(.bottom, 8) Text(context.viewState.title) diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupKeyBackupScreen/View/SecureBackupKeyBackupScreen.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupKeyBackupScreen/View/SecureBackupKeyBackupScreen.swift index d578bcd9b9..63f7463b93 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupKeyBackupScreen/View/SecureBackupKeyBackupScreen.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupKeyBackupScreen/View/SecureBackupKeyBackupScreen.swift @@ -40,7 +40,7 @@ struct SecureBackupKeyBackupScreen: View { private var disableBackupSection: some View { VStack(spacing: 16) { - HeroImage(icon: \.keyOffSolid) + BigIcon(icon: \.keyOffSolid) Text(L10n.screenKeyBackupDisableTitle) .foregroundColor(.compound.textPrimary) diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupLogoutConfirmationScreen/View/SecureBackupLogoutConfirmationScreen.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupLogoutConfirmationScreen/View/SecureBackupLogoutConfirmationScreen.swift index 0c682c53c1..6db15cb4ba 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupLogoutConfirmationScreen/View/SecureBackupLogoutConfirmationScreen.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupLogoutConfirmationScreen/View/SecureBackupLogoutConfirmationScreen.swift @@ -15,7 +15,7 @@ struct SecureBackupLogoutConfirmationScreen: View { var body: some View { FullscreenDialog { VStack(spacing: 16) { - HeroImage(icon: \.keyOffSolid) + BigIcon(icon: \.keyOffSolid) content } .padding() diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift index c3c0bd1ac5..00f980ab5a 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift @@ -53,7 +53,7 @@ struct SecureBackupRecoveryKeyScreen: View { private var header: some View { VStack(spacing: 16) { - HeroImage(icon: \.keySolid) + BigIcon(icon: \.keySolid) Text(context.viewState.title) .foregroundColor(.compound.textPrimary) diff --git a/PreviewTests/Sources/GeneratedPreviewTests.swift b/PreviewTests/Sources/GeneratedPreviewTests.swift index 615b072af3..84726015bd 100644 --- a/PreviewTests/Sources/GeneratedPreviewTests.swift +++ b/PreviewTests/Sources/GeneratedPreviewTests.swift @@ -83,6 +83,12 @@ extension PreviewTests { } } + func test_bigIcon() { + for preview in BigIcon_Previews._allPreviews { + assertSnapshots(matching: preview) + } + } + func test_blockedUsersScreen() { for preview in BlockedUsersScreen_Previews._allPreviews { assertSnapshots(matching: preview) @@ -221,12 +227,6 @@ extension PreviewTests { } } - func test_heroImage() { - for preview in HeroImage_Previews._allPreviews { - assertSnapshots(matching: preview) - } - } - func test_highlightedTimelineItemModifier() { for preview in HighlightedTimelineItemModifier_Previews._allPreviews { assertSnapshots(matching: preview) diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPad-en-GB.1.png index 9fcaf0bf75..1ce6f753f0 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c2db75b98f86afcffbba913ef8d728d6a6ab51d3b73314897c1108210d00c45 -size 517693 +oid sha256:8b702610b9a43191c4dccee0173300c5c5db9041e60ad9bc581ebfc2a47fcd8a +size 519748 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPad-pseudo.1.png index bbf759b2f2..3257c220a6 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:87469d91baa3ee639d04012ad9afcf049eb8e9c6cd78fb6dadbac5fbcfb481f3 -size 543421 +oid sha256:2151c4124c4f826a9b2b1039ec2cffea1f7f9930e37918ca65558239190c8032 +size 545056 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPhone-16-en-GB.1.png index d1778a8a09..fa61a7ccfb 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8f77a8e5c48eb636514ce6f735c79d583c4ffc7b034cdc49484fbff0ec4a882 -size 315619 +oid sha256:76a550baf0bfc5fc2e7d2ccbfdb1e367cd025a3daca8526d4040a46523454869 +size 316889 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPhone-16-pseudo.1.png index 464eb10636..aa1e5701e0 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0f75923ed4fcfb1c774751abe0a5697fbbf71a274a163dce56c1723a1e6ead2 -size 321695 +oid sha256:8e28e24910ef83b66f273751cb0427e7f6df1ac127bfe6422ca172e7be0819bc +size 323831 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-en-GB.Confirm.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-en-GB.Confirm.png index 57a11fe679..f51e581e4e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-en-GB.Confirm.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-en-GB.Confirm.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e8e2c0d5fb539547eb17874010dc890a90283cd99a9baf63a91b8b4d4bccfc1 -size 102419 +oid sha256:0aaae68e8c63927afcd5320c2657c025d1982b11debf3e9a5925ee5b9580b0f6 +size 101720 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-en-GB.Create.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-en-GB.Create.png index 3aef94e6e5..6d7374e01c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-en-GB.Create.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-en-GB.Create.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a1da726841138e630d6e85133cda1fd90baab69fbaf6513c465d4e56ead739e7 -size 102732 +oid sha256:f7cde3e4e50e9fc852fc44f521b3d629d2d98f73ab3b3221aec32ae54d339594 +size 102036 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-en-GB.Unlock-Failed.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-en-GB.Unlock-Failed.png index e4e1bfcdce..3227ba59b5 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-en-GB.Unlock-Failed.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-en-GB.Unlock-Failed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13118c3e920fdee262b388f2ed710f631c06502ef2447074c9c8551e2939a8ff -size 92803 +oid sha256:a89061d4ba1b7c8cdb80f3e2eedc3d2f35af10060af395d6ee68ecf8596c518d +size 92227 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-en-GB.Unlock.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-en-GB.Unlock.png index c1138af9bb..4cb3ed0551 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-en-GB.Unlock.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-en-GB.Unlock.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c25a877355d3256f14e6029a877bdf86cff7b792b7639e33acb50fa09de34987 -size 91322 +oid sha256:3e3141859adc07a1a7194db3aa89045caf0b6fe682d4948f21a3c9fc902aae47 +size 90769 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-pseudo.Confirm.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-pseudo.Confirm.png index e6cc07ef3b..d97539afa1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-pseudo.Confirm.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-pseudo.Confirm.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c96e282d280d870f4e5f625381034ae37572fd3f84a88314ae2ca1d265d4fb28 -size 120848 +oid sha256:1dda7ef374d7784aa0ec87123e04d1325ca15a8fd3128a09788dd6f4fcd5435e +size 120397 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-pseudo.Create.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-pseudo.Create.png index 294a4f6a23..0ac512863c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-pseudo.Create.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-pseudo.Create.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:268ba715124976362a3bffb717681af0d82b8e93562d2374a70ff2bf4cde752f -size 121144 +oid sha256:bb0662eabc3099f39991a6d22a4a3e1a2d7b20175c365452cef0155399b1ce39 +size 120642 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-pseudo.Unlock-Failed.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-pseudo.Unlock-Failed.png index 34c82523dd..f427d0bcce 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-pseudo.Unlock-Failed.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-pseudo.Unlock-Failed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a7afee97b88a11735eaf6a7feebc86b16d48bf9c07e5a4842d05223f542d0b0d -size 94251 +oid sha256:c20ef9f91eaf5b50893f227886a55de5589d4fd48779c52cd70f7ac349b6cf55 +size 93704 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-pseudo.Unlock.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-pseudo.Unlock.png index 49eaaa08d6..d60bcb214a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-pseudo.Unlock.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPad-pseudo.Unlock.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82e57447f74abc770a97c13e2c68cff73c02aa46321596d620195a18de078691 -size 93896 +oid sha256:1f2418234c9e398983b2fe2f9812be025eff010cc1f8bc8f2547afe67ced150e +size 93336 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-en-GB.Confirm.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-en-GB.Confirm.png index f36f90c0dc..4c071f0f16 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-en-GB.Confirm.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-en-GB.Confirm.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f0d14bbe0566bc50d964af9f958c5501d8905748299545e54e36ed41dc63696 -size 58511 +oid sha256:8927accb451011658d3eaa3e677764b8df123b8e0fc2d658a3b9c2ab56cc46ef +size 58405 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-en-GB.Create.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-en-GB.Create.png index 2e6216a984..f08834c954 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-en-GB.Create.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-en-GB.Create.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd3f9345a6ee5874078da7ab4ea83d6c8f1cb608bfba421730d6228ac84cdc75 -size 58844 +oid sha256:8c219f072b9d6afe480672105dffb966b257453b275a03ad3565166708a2039a +size 58700 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-en-GB.Unlock-Failed.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-en-GB.Unlock-Failed.png index df2b21ba77..53c410b189 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-en-GB.Unlock-Failed.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-en-GB.Unlock-Failed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e7629b409f37c0fddd6f36e68d6fe6da2aa9d782759abbd74501e80bb4cb35a1 -size 49415 +oid sha256:07b296f7d747cc3c2c02e6bfadfdd026505f1057176187c708cedefc87b7c240 +size 49207 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-en-GB.Unlock.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-en-GB.Unlock.png index ee6398ec05..c5955f5144 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-en-GB.Unlock.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-en-GB.Unlock.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:03ebe2f4f43ff78ed48a4e2cc943ddbdfdfc6e07944804b55fc75ae855a76698 -size 48104 +oid sha256:69321186112474b6f2e74382d7ee2de6f019111dee666f39bdc00041fee8ad41 +size 47793 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-pseudo.Confirm.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-pseudo.Confirm.png index ef3602320a..f2ed6c6cd8 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-pseudo.Confirm.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-pseudo.Confirm.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:51d4fe7837ee09f20bb446037004354bdd7e3056f37a72e8f63c074e691addd9 -size 79170 +oid sha256:9e352074ae172e506d02ac4ec96a8a5d1dfb38184489e48296ca349ecc7796f4 +size 79077 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-pseudo.Create.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-pseudo.Create.png index a25ca847ec..30a12642d9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-pseudo.Create.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-pseudo.Create.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ab0ad8f12771b5636df311f435c05af4a9f965438426f72899bf9ae31b10d0b -size 79454 +oid sha256:8e7937cbeae8a4c53a51ac85891d28b43d1346676493969751add9c40abe0e84 +size 79363 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-pseudo.Unlock-Failed.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-pseudo.Unlock-Failed.png index 395d5e1f35..fe7ac85278 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-pseudo.Unlock-Failed.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-pseudo.Unlock-Failed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d3f3fc20d010027cabf991f4ed7e626ff3cf3b37a27f533689e13e058128044 -size 52015 +oid sha256:a65aec7ba0447b622e1a051c4271adafe2bc73c43dff06a7d470ced000fa9a77 +size 51766 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-pseudo.Unlock.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-pseudo.Unlock.png index 11c7b7124a..b435179032 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-pseudo.Unlock.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_appLockSetupPINScreen-iPhone-16-pseudo.Unlock.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8bab864c1f3986d0c09972e4793d5ca18976ad533a428f006192c8c412b260ac -size 51822 +oid sha256:2fcfefc8b08089b1bfaf7ce157b4f0d08fec7a2bf15d8ac4bb8dd4484d4e8699 +size 51549 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_bigIcon-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_bigIcon-iPad-en-GB.1.png new file mode 100644 index 0000000000..17416887e4 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_bigIcon-iPad-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd3411701b93dd7452d099b533ce08b1c69d2207af736ab1269cf7386295128f +size 86763 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_bigIcon-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_bigIcon-iPad-pseudo.1.png new file mode 100644 index 0000000000..17416887e4 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_bigIcon-iPad-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd3411701b93dd7452d099b533ce08b1c69d2207af736ab1269cf7386295128f +size 86763 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_bigIcon-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_bigIcon-iPhone-16-en-GB.1.png new file mode 100644 index 0000000000..67c9f1414a --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_bigIcon-iPhone-16-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b0c07456ae43e1a3e11aea244a8e827b4abf670b12018537d87ba5cc83e6069 +size 45805 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_bigIcon-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_bigIcon-iPhone-16-pseudo.1.png new file mode 100644 index 0000000000..67c9f1414a --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_bigIcon-iPhone-16-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b0c07456ae43e1a3e11aea244a8e827b4abf670b12018537d87ba5cc83e6069 +size 45805 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetPasswordScreen-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetPasswordScreen-iPad-en-GB.1.png index 1b7ee2757d..c702865fde 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetPasswordScreen-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetPasswordScreen-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e43762e16cf7619be64aff1f0510ce618fb6980248df235bbcf4d017d9e94f14 -size 100528 +oid sha256:5f2d161033e0af7ddc616d0eb652020b7c4e9a87c646c0734d06d7bb0e8a9a9d +size 99948 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetPasswordScreen-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetPasswordScreen-iPad-pseudo.1.png index 55cd644bf6..1f4183c858 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetPasswordScreen-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetPasswordScreen-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86d58428192333e6aa8697a1e22c115bbbea4183e29100e4e75ea6fc1c377c4c -size 117766 +oid sha256:cda9062039283132c3a793e896d1ab039cf9c646d4fee267d905faf7dcb64735 +size 117332 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetPasswordScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetPasswordScreen-iPhone-16-en-GB.1.png index 999facff08..c5b8713493 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetPasswordScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetPasswordScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a83ab0654a14096806604c8ba2beb2e762651799283b0313af34030634d2c025 -size 56895 +oid sha256:18ff87c5d19917060e6fa5f0df10eda9ff5aa78116cc821762070b1da71e879e +size 56640 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetPasswordScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetPasswordScreen-iPhone-16-pseudo.1.png index 6e2b095584..795f06b882 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetPasswordScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetPasswordScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af2422461ff5afc9dcd3f26a6cda3801ea64c163644f4a02bef28ac5fe2363c7 -size 73087 +oid sha256:635ff740d1a3c9bf2b4e4e58c23ace908a732d408fccfa6cada3e031766851bf +size 72949 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-en-GB.1.png index 6c6e33a78e..2b2c61bdf3 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:35ff1ec5ba08bdcfe19c68655ea38c0a679ee896aee25b188b9667439e37c605 -size 157122 +oid sha256:b6669bd265a9b14b47f3e80786ce487275a81dd0a7c096bab707fc573d5ca5ba +size 156868 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-pseudo.1.png index dbe3171aa9..cf3c41e801 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:01977f47e883168370867511229e4357a7a9c3fb5b1ec623fc618c6627c0b1c4 -size 213205 +oid sha256:13eb76a6174949e63d35e260744763a405b7fd4cdfb0405189b647115be04f4d +size 212875 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-en-GB.1.png index 43ff30697b..0e1b4d78c0 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04e69f0f8f95eef2e94d6b5ff06340b2961c19f82a65285330e8aa4299d4b7e3 -size 107557 +oid sha256:e2f9383e9ccde9ed8fda0461051e0c445537c299f3881b2d7feeae084bdb6be4 +size 107493 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-pseudo.1.png index d0f4fa5900..a20c9ecf3c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8bdd88fa80104946db0c54b75d0a2b95d1283152f6a640b4de24428df41401ff -size 164076 +oid sha256:359d2bfea87562d0bce3472e9994180397a2b33315b9d6768439bfbdcc3eb61a +size 164012 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_heroImage-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_heroImage-iPad-en-GB.1.png deleted file mode 100644 index 8a22529d51..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_heroImage-iPad-en-GB.1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:798f00aea1c63551ad62f6f926fac78bf6a52b051a84700205f8013b121c8c5d -size 86845 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_heroImage-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_heroImage-iPad-pseudo.1.png deleted file mode 100644 index 8a22529d51..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_heroImage-iPad-pseudo.1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:798f00aea1c63551ad62f6f926fac78bf6a52b051a84700205f8013b121c8c5d -size 86845 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_heroImage-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_heroImage-iPhone-16-en-GB.1.png deleted file mode 100644 index 74cb603767..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_heroImage-iPhone-16-en-GB.1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:df39d35a4bc393f96bec96b9741cb1719faad3f39108e4efc74ddbd542a01b6f -size 45643 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_heroImage-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_heroImage-iPhone-16-pseudo.1.png deleted file mode 100644 index 74cb603767..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_heroImage-iPhone-16-pseudo.1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:df39d35a4bc393f96bec96b9741cb1719faad3f39108e4efc74ddbd542a01b6f -size 45643 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-en-GB.1.png index 8dae69713a..cd75a1a54c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:25d8ff95556fe43a082fa3b9608ee24c9aad7d7ba6301c08ec379a05454a8845 -size 115469 +oid sha256:c664b1a6087e9d8ca0b3800d28ec524ea2c673f2af29ea3a5141b3c90a75d0cd +size 114821 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-pseudo.1.png index e314b7dfc4..60da243f8d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:37ec779f712d787c348388eddc70a81616f7d99b8341afbd121a77a332b42a62 -size 127877 +oid sha256:65c9523f7de175bc72bdf9ab54ba912434cb38340ec9dd6be7f9a7ba4a3de774 +size 127199 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-en-GB.1.png index aa3e1de658..e3934f94cd 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94b7e221c0c46d14eebd9c91e3915ea24819fd0c80d75ac73c2734f15a6a1881 -size 68993 +oid sha256:e7110b9d71eaea56f760c77e50d33b0ad0111c85259856eee78fc5b47d4ee892 +size 68645 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-pseudo.1.png index d2501e83bf..57489cf00f 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmationScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3126e413b4fa4d38ce378a479e3ff5d34383cf6dea456582eb2806f1e5ff2d4d -size 87719 +oid sha256:36486eb38870172f99df27faa7d81e6f60070420443a20335927e8fa650a0232 +size 87491 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmedScreen-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmedScreen-iPad-en-GB.1.png index ce8d89078f..5e3a5366e5 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmedScreen-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmedScreen-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:accd166f617c76fd93fb981e22091b0fa6fffb29800a2f9f9a52d97ee168070b -size 96564 +oid sha256:e230f4c7bdbc45ee05e93cd98f0f22a4800d9397428f161604b9bf0e89722c61 +size 95961 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmedScreen-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmedScreen-iPad-pseudo.1.png index 269f0ceb99..0a8cc4f348 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmedScreen-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmedScreen-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8bde634539569c48796d0edda577fe408b04ddfa4124268e4e37d5b45a13410c -size 109812 +oid sha256:ce8b5b2f87c552f7331874a0d12258fa0c158d095b194fcc6b043bd5faeeb8e0 +size 109013 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmedScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmedScreen-iPhone-16-en-GB.1.png index 2944131d01..89ea93fa4e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmedScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmedScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b53456c363be86017d2b9e78f206cd11da7f7eb289252f3889a198408d6d74f -size 53533 +oid sha256:a9634c0047fa3928b4cc3662aa8bb1315900f89f74e845b5d074d06e68d9da98 +size 53028 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmedScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmedScreen-iPhone-16-pseudo.1.png index 9870df8772..a6199c9b06 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmedScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_identityConfirmedScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2013ae2ea2e2c287aefb1d325557226f8734f6c3fc15f148eb5b88d76f372f7 -size 68968 +oid sha256:97d333e6798cd7449999133c69480f6162011f327974d49fbbb7d47c786db9d5 +size 68742 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knocked.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knocked.png index 16efb5ef59..36123d267e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knocked.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knocked.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79f1c8ea20c6791a7d25d8fc18f45d1d0fef989b8fa0316c5b312bfc9dd4c271 -size 1972382 +oid sha256:47bda34db0009a5f2d04a9ab3fdb761d9efb266046c85abf786051ec3d30f081 +size 1973723 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knocked.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knocked.png index 65181a2704..c32281cae2 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knocked.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knocked.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d159ef72270668acb4ffe1443954086a895e630d5809ff51a65b9302ada8ca89 -size 1988527 +oid sha256:7d768cff2a5e42c223808f4fc3b1b3f7fbfe13d94711ae2ddeb77d24c2088517 +size 1990759 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knocked.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knocked.png index 4e8d7e6c1c..7f840a1715 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knocked.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knocked.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e0a6de1d32f7d2845bf66ae655ac2b7404083a6d3478f96e1eea29f0b102e51 -size 811030 +oid sha256:6f21b9720b09a4fdc89f4f1f25548200d159b404ed6431693defe99c401f99d2 +size 812339 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knocked.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knocked.png index daca3b20bf..81a9e865f9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knocked.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knocked.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e8581d10df35158cd98cc36e23f1ca1ad38659732699bd7073f92aa4331e847 -size 828204 +oid sha256:7ae617786ed8ee75c55bf33222bdf80faca1dac30a5d0fca84175e3ed7f42d9f +size 829745 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.Credentials-Entered.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.Credentials-Entered.png index dce2d64cd6..f075c88734 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.Credentials-Entered.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.Credentials-Entered.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:268e4db4b7fbbabd0804c70d5532c8632ee225d633304da1e136c489332ab306 -size 92683 +oid sha256:8d85af5b139ae8af8f98acc7a366f51f644ee4e4c32087a9f38895131a5cf3ab +size 91146 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.Unsupported.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.Unsupported.png index 93f5919749..2f1e1d0679 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.Unsupported.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.Unsupported.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d93585f86e776c0636eb9457cfa6acd35dfef6a8e9591b817aa9b41470ffb1cb -size 95029 +oid sha256:b3a1054d741c88b6478b2befe8f1c33be87a57721a5cf32912c2bb62def9e929 +size 94504 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.matrix-org.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.matrix-org.png index 737046c6c0..48d2bffdc4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.matrix-org.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-en-GB.matrix-org.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9369cd8554cdfc4d1e53d73b33a890db9c7f9e8e39fe4075a58999ec5b2df8ca -size 95979 +oid sha256:42c3d1d51c27e4c9ad7d35688d4050d97fd7d165e77223e609c82dc6631b7ac6 +size 94453 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.Credentials-Entered.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.Credentials-Entered.png index 393349d96a..4792e503c2 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.Credentials-Entered.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.Credentials-Entered.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7821d479f4e7230da2df271d889e5c6147edadfbd2b9cadd391a7959605d74a4 -size 96911 +oid sha256:eb3dfb1e2dedb8ddc54ec7fcadd270f62391efa6906b93a5857908dff17a1bad +size 95331 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.Unsupported.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.Unsupported.png index f87f254ae6..cdc135098f 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.Unsupported.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.Unsupported.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:374a57ae7fcf402b65b10a2863b0a05128ac111b97b0ee88298d880f1fc8fb7c -size 116598 +oid sha256:f74db8270e8cf70c0c9151faf717f9fd146287d5a984421adab858917c6242cd +size 116034 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.matrix-org.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.matrix-org.png index d3cc66a102..120e71f320 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.matrix-org.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPad-pseudo.matrix-org.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:415182fb2d280fdd1c6b723383621acad2275b1d2286de57e718e21c274e3823 -size 100924 +oid sha256:7dd98bf3e8d9450b90902c5d9b78c33fc01a9cf76f1bdb3084a619b97bf09f04 +size 99360 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.Credentials-Entered.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.Credentials-Entered.png index 01556de3d9..e340b9a863 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.Credentials-Entered.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.Credentials-Entered.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f305e0b0e2423bd83e825f0a5fff222fc751ba9a9a397a48ec8884191fa649af -size 47648 +oid sha256:69f9a72211bb4e8865d5e944a39965bd8ae106e3e5ab978a7e125be43b4db77a +size 47422 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.Unsupported.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.Unsupported.png index 8787a8792a..e51078cff2 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.Unsupported.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.Unsupported.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c25500ebf350efd1e8fee87c2031b7caeb6e525e22a65db6d793301749a8474b -size 55931 +oid sha256:541601f6d62456324f58f1ef5021567f22d2cb3798007814b7962708a86722c6 +size 55565 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.matrix-org.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.matrix-org.png index b46de1923d..3a6bd61b01 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.matrix-org.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-en-GB.matrix-org.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b57da444c1ba2db6cc6cc5f465753f56911079916a857d63435b79f1c64d6e00 -size 50637 +oid sha256:1e8f48a5189f53a45cf504da4d6828677df479036011660e218fa9582e6a53f5 +size 50317 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.Credentials-Entered.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.Credentials-Entered.png index 8bc5c3f3b6..8e595e7fa6 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.Credentials-Entered.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.Credentials-Entered.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:40434e10facf844daa52896c3cabb1ac9dfc05cfb1d25ea358ca1f0387012655 -size 55739 +oid sha256:51afd42d91ebfc2b63a8c961f7b9f34dcfa1c6cee075ef361354384cf994bddb +size 55059 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.Unsupported.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.Unsupported.png index faba525b6b..39006d4a3b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.Unsupported.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.Unsupported.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5eebc69dcd0285b9e83b7594f157f8aa45ebe217d2a186e981d821dd81847d46 -size 80515 +oid sha256:f4b9ccdb9656c2d28d91c6d3a3d65af0b40cc049977ce6cdf3ba2bd3943338b0 +size 80171 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.matrix-org.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.matrix-org.png index 974eeb50e8..2a3f6df701 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.matrix-org.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_loginScreen-iPhone-16-pseudo.matrix-org.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76dbdda9f2d9b68d64a912d8d1b3e945449d71e6c5f557ae426d97e9b6b3a3ae -size 59817 +oid sha256:b311840717a31f6fe7a70a6bf61a8a2556941bdad9c2caee94dbea781fe5fe76 +size 59181 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_notificationPermissionsScreen-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_notificationPermissionsScreen-iPad-en-GB.1.png index b9fa917c65..db1b01cf54 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_notificationPermissionsScreen-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_notificationPermissionsScreen-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f0e46d684e61d7c71d57edef6050678c5138866c3b56a5d1fceebec3344f308 -size 446397 +oid sha256:9399fed5f2a4033b1d2eb68ba85a1171466fd13c13caae0d721bc3aa02ac0441 +size 448049 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_notificationPermissionsScreen-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_notificationPermissionsScreen-iPad-pseudo.1.png index 0e48cca107..f3dfb000f4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_notificationPermissionsScreen-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_notificationPermissionsScreen-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d94207b315052bf345acc8b842444f3bde379ae8c210207570785d26c64a0012 -size 471518 +oid sha256:ab0c1142ee57473ba9ac24cb0fda2015c2f30ef00e3561a931c4b0928d7df77b +size 470178 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_notificationPermissionsScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_notificationPermissionsScreen-iPhone-16-en-GB.1.png index 8523dc8deb..4b7edd42e8 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_notificationPermissionsScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_notificationPermissionsScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c864558ebed0a41cb5120509923876d183932c4219cea89c2cef28a061bd41c3 -size 292807 +oid sha256:40d1ab56e337e7bb924e9f757c1f1c479163c3b00308fb81d34692cd8196cd8c +size 293583 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_notificationPermissionsScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_notificationPermissionsScreen-iPhone-16-pseudo.1.png index 644029f6c7..6a53a4ba4d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_notificationPermissionsScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_notificationPermissionsScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:202d4983b945f41ce84f96292ca7f1f65b85349d7837673bc07f3d61c0ac5c40 -size 308383 +oid sha256:c1e43f3a98ea4f830ee9110028646c362e1be4175111ca60a4b359f127d9224c +size 309530 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_pinnedEventsTimelineScreen-iPad-en-GB.Empty.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_pinnedEventsTimelineScreen-iPad-en-GB.Empty.png index 0bd69fb870..a9a238a53c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_pinnedEventsTimelineScreen-iPad-en-GB.Empty.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_pinnedEventsTimelineScreen-iPad-en-GB.Empty.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1cb783734a65fa84449c22e1c2df60151ff03e17a9a6d7f7ada2568be24a08c9 -size 96586 +oid sha256:779fbc896e06aaabcbd630fbc37b1b7ca7c4b5d24aca5013e7cdaf2f5c7affb3 +size 96245 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_pinnedEventsTimelineScreen-iPad-pseudo.Empty.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_pinnedEventsTimelineScreen-iPad-pseudo.Empty.png index ab452f8252..be5dba08ba 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_pinnedEventsTimelineScreen-iPad-pseudo.Empty.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_pinnedEventsTimelineScreen-iPad-pseudo.Empty.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cb254d38a6bd5eacf43837e030ec62fa12489daa4f4b8b838b71bec9b7a469e3 -size 112922 +oid sha256:7c122f1df55e7944efe4a14d3540fce1298e4ce1aa7578840c211cdaa4572f1a +size 112566 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_pinnedEventsTimelineScreen-iPhone-16-en-GB.Empty.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_pinnedEventsTimelineScreen-iPhone-16-en-GB.Empty.png index 74d01ed9ca..da57ca1bef 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_pinnedEventsTimelineScreen-iPhone-16-en-GB.Empty.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_pinnedEventsTimelineScreen-iPhone-16-en-GB.Empty.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b35ed703fbe9bdcbd7fbc637f0954ceb66a316e534cc331920fab34522758e7 -size 56642 +oid sha256:c41242a8fc2df8bf2ee6be525f2d9247e9f4333193098006cd768f2f6d44fa09 +size 56386 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_pinnedEventsTimelineScreen-iPhone-16-pseudo.Empty.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_pinnedEventsTimelineScreen-iPhone-16-pseudo.Empty.png index 082c188f48..139df2a7b4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_pinnedEventsTimelineScreen-iPhone-16-pseudo.Empty.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_pinnedEventsTimelineScreen-iPhone-16-pseudo.Empty.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:859d4cad624b9556ad55f8c4d39badd26512237c280ee9affe44321c162cba18 -size 76416 +oid sha256:f01f3a67d066a9b4f53a2f11575e7ee73207c954b4d07557743b2da84cd05253 +size 76063 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Cancelled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Cancelled.png index 438909e9ed..2233357cae 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Cancelled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Cancelled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d2b451eb6bc898a85a3d70d00cdc3da623f794aa6c8a4f15d8409449a945845 -size 100792 +oid sha256:614193c4d202e37a09e4cc027f367704a0f459da4c19e5dfc5e455b9bf1ebb91 +size 100487 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Connecting.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Connecting.png index de84ddbeff..417c73834a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Connecting.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Connecting.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9af5feed468c1e193d153cf3a79464c2594d11c143a143bb6d1d0e2167456801 -size 106015 +oid sha256:5118050501a46c00143ef43742be4cb239b80a46ff94bc0f3e079f774ba3ac02 +size 105351 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Connection-not-secure.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Connection-not-secure.png index b64d08e784..7a7baaf94d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Connection-not-secure.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Connection-not-secure.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b7180ebbb140dea72b006104c9e76cdbba65a8281ebf1df2a0037eca668fe75 -size 160208 +oid sha256:2438be958a801fe7938c2e7df5a0ced027f9179ca1d42479d03b9db25a97f5d9 +size 160064 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Declined.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Declined.png index 1e3ba731b0..eb78990c92 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Declined.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Declined.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c20f3df009d815ccdf37db4af789c400be29041153f465e9f71cf551818ebd6 -size 98682 +oid sha256:123406b9830503244c28536110ee449007f0eddd70261f46ca6717b21e7899d7 +size 98416 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Device-code.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Device-code.png index 4c777b4a2b..bc0c00bd4c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Device-code.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Device-code.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2424cd9bb0c636eec98a52603bcef3cf7d334f12efbdb74e0df0803d35ca1df -size 117014 +oid sha256:6cc16bf32cba121e8614af30a879c877013d68c2c25b5801f236c6d76cda94f3 +size 116404 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Device-not-signed-in.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Device-not-signed-in.png index 9bad246cb8..fc094f838e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Device-not-signed-in.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Device-not-signed-in.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f1580204688212fd8a948143715ca5c0b6585051c6f2b4dc7d4306a19d2ee646 -size 123145 +oid sha256:48b84efc510d0de306ea99cd726f79e12e1988ae8ad3fe52ef3c6348a096d6c2 +size 122590 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Device-not-supported.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Device-not-supported.png index 50d4425f1d..f50baf5da6 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Device-not-supported.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Device-not-supported.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49efc224c55e165a7bdbc3c4036f8ec3a111293fd7eb73131b016eb99a8cbb26 -size 100886 +oid sha256:66da381173bdafd89500442ff09d56f6f0daa8a8f1b127d60defd6ad4545e545 +size 100725 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Expired.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Expired.png index e7b40fbe82..a1b7004182 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Expired.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Expired.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b9bcabb6711e7d21bd9bfbb5b620051019d333b8f7694ede5351b0e88e74ab35 -size 102101 +oid sha256:90fd278cdd95fe12eaf2e9c739c69d9512910c1da99864b508b0f2a41d0fdf29 +size 101925 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Initial.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Initial.png index c0721c9204..c68afdc757 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Initial.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Initial.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8eede65da7fcb800583f0f73a595cadecdb233f3185f7e272797b63e39f25df8 -size 146499 +oid sha256:2721166398d184d26983211c370bc4f6ea9e1d280f3466945bfe1c6ae5420528 +size 145904 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Invalid.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Invalid.png index 5dc2ae2d27..093df7c3d1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Invalid.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Invalid.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:03c18c209c7d4ad65dd7cad0f01e09df0ac8655029ef0bf06697269bbca7980b -size 117845 +oid sha256:15bffb921a394273401bd228a3bfe30a8e175f16111ce5f7001aae94ef22520f +size 117254 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Linking-unsupported.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Linking-unsupported.png index 20c035b9e8..308fefbee1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Linking-unsupported.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Linking-unsupported.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e52853f72f1a139274fdefcb94f0fd9c6364c43e5808f6ca504eb201f4d3b45 -size 120877 +oid sha256:2a87bd66f523175b80a2c1cbe3a290626f5eb9d397551ffa20590093e662099a +size 120570 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.No-Camera-Permission.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.No-Camera-Permission.png index 5e4eec0800..4fc704bbbc 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.No-Camera-Permission.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.No-Camera-Permission.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f6a456aac2ce6a7e01d638dfecbe7fdb298fee88640b7a95388398b954a22e7 -size 114363 +oid sha256:a303ae2ee0f4328d3b3b3d59b26dd1f31445d6de52400df4f5ea02062e61e4ff +size 114228 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Scanning.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Scanning.png index 7fc4a0b74d..81aa7cafd0 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Scanning.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Scanning.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c239ff3cd5c449464fbd829d6af9285371ff255ba5afeb8838acae0c2741f78 -size 98654 +oid sha256:aa98976b4a051e18b872b2477cb903291c2a5b415900cce70dbc171bd0a56af5 +size 97995 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Unknown-error.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Unknown-error.png index d8e37ce7a8..dcccc858c9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Unknown-error.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Unknown-error.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b78ed1fdff7c96c641a6ad6af5857a2b03bfee681e0d00aaab8ad9e0c07fad6 -size 100793 +oid sha256:c47d97b522ee217f7a9539f14e76144beb137d94248f9272ee337bcd2f247182 +size 100580 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Verification-code.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Verification-code.png index 6fdcb1907d..8c1db590d8 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Verification-code.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-en-GB.Verification-code.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86a40b993a17483418b5619860d7f48a08ddc86e352b0c7fabb573094c7b0222 -size 122309 +oid sha256:d3077e30e71cce589bb0f371b4cde9b0cc5c5be07b742c1bc545e2e049621570 +size 121321 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Cancelled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Cancelled.png index a92e48d02b..bd691917f6 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Cancelled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Cancelled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:73d6c3ff715e4127bf5a52c3c4a9e81a2ebc3cc98be1ff55fed09ee6c037b907 -size 110007 +oid sha256:1b9ec87ce68864239e1e41c26722485efbe120a3f3cb610137f7e9c2c851a9ac +size 109865 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Connecting.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Connecting.png index e050959d38..1479dd2379 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Connecting.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Connecting.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:60ac74eae987ba9a7153a33e4586f083619c20bce62a2da426aa77f33a21f5fd -size 110001 +oid sha256:c87bbd389e12082c07ac0846b7a571dc3b5613b12824e1180a09d02df2fd2f97 +size 109374 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Connection-not-secure.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Connection-not-secure.png index 866ff46c68..975d0f9a05 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Connection-not-secure.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Connection-not-secure.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55be20cbeaaa9fcc334698f0fa741edb9cd3075c66ed74e05874f51632c168ec -size 212452 +oid sha256:afeb94794158a4a94e286a824403b6a1a608f4af9ee5a82d0a189567ae9bd474 +size 212113 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Declined.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Declined.png index 1c05261a64..96ab128e8b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Declined.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Declined.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eb8992d471bbaaa0867bc754d7aab26a573d3c89a72f22fcad6d42a965150c00 -size 105004 +oid sha256:693145579699d9ffaa9e5adc90364e5e55d7273e9d0b9e014a4df9e727d8d8a3 +size 104701 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Device-code.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Device-code.png index 34dccdcfd7..13c5e0a073 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Device-code.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Device-code.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a68e7dc0baf9bb74a67366ce8fb7e40bd643ffb58e269da4d40c2dfa4c9c1c1 -size 139006 +oid sha256:17d3853578f9900f0c6c643017ef68a26129dc731eef1df29d613ed534978a1d +size 138423 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Device-not-signed-in.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Device-not-signed-in.png index cb6c065fc3..03e429e2f7 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Device-not-signed-in.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Device-not-signed-in.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0daa4ea03ca52a96ce1275d61731ac4a5bcbdb812c7f1de4f65500d26f41235b -size 138929 +oid sha256:bdaa4f631dfc2c2d3661cdada9b38983187e93cbbc89029362829bb2620528af +size 138208 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Device-not-supported.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Device-not-supported.png index afccde2333..d426e93a16 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Device-not-supported.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Device-not-supported.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b4514e48b1e75b3580870ac166df28ea45bd6e5d28b94e5868e8c171cf9732f3 -size 111431 +oid sha256:0bb04bad6b2daae4db6d76fa1e287ff88accda0949ab0d175d9e1e5a2069be3a +size 111244 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Expired.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Expired.png index b3890f255a..6b881f4986 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Expired.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Expired.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1956fba895114fc09086604a0288c4f5c4654ccdde24281685c91f08570af4d6 -size 110645 +oid sha256:1c7c9699ed61752a13b91f7c8fb9d1ec7d4d6ed5fc9268583a1564d58a3ed6c2 +size 110585 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Initial.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Initial.png index a810311f22..5aaff947e5 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Initial.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Initial.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:695b6d2b5859fb05242e2c6450f69d5997f113ddd0346c12df21bb11d2447f96 -size 180110 +oid sha256:bc3e0790ee7dcbc0801fdb324423ac11a6075c384a2b3a455dcf3695022633c6 +size 179327 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Invalid.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Invalid.png index ddad41c250..75f9aa515c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Invalid.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Invalid.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:38ab23ba7e3aee25b9633c702f0608f0db5431416cc866e147b9eedbdcc70c3a -size 123065 +oid sha256:3dc8b478e57c1be6ce64df3fbc7af4ccd65ff39d7ee26f5bef81797416d4fa8f +size 122522 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Linking-unsupported.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Linking-unsupported.png index fe7131f254..56ca39c7d1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Linking-unsupported.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Linking-unsupported.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6811e82ac591ec69983a55164a31c6102136df74bbac7b55f20868e382928909 -size 143905 +oid sha256:947cc5cdd9c505188af4be194107fb891c533150324fdf2f86e0bab5927e49c3 +size 143531 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.No-Camera-Permission.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.No-Camera-Permission.png index 164bec2b13..2795f0ef7e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.No-Camera-Permission.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.No-Camera-Permission.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0159c1185c43a3a5e6844f6a59704c0f82966163133f87b9652f6cf5123c528e -size 138166 +oid sha256:230c41e326f43e645d125f76e0a3c28e9b81cebd64b88a86b57eabb26e20d169 +size 137503 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Scanning.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Scanning.png index 5ceb53f4f1..b2bbcb5cf3 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Scanning.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Scanning.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ea6d61cb3cfc083c75f709fb51a0e4a9e0370634200664ee281397adfad35cc -size 99575 +oid sha256:e0aeeb0981996df5b654e8930ce8910c0a84f697afcdb59028ba87634319c1dc +size 98904 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Unknown-error.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Unknown-error.png index ec9ba7e9c4..9920b04efa 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Unknown-error.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Unknown-error.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4f80d33fa6b1da9455d0fb993b2750ef0d3100564d98b095c422e4475baca64 -size 109524 +oid sha256:1cc91630f9eb6ff95deea48ddc9a6c032d06d965d644cd4a9d5526dd67834518 +size 109361 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Verification-code.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Verification-code.png index 092800bb2b..0a0899ac49 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Verification-code.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPad-pseudo.Verification-code.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9f0b87391733076dedd6104648158fc7953e991ddea2b67d1b14cb908e8b53bc -size 138793 +oid sha256:33905919785a8c59ec31dab1528e31efe07d57ae1fb1ded4ac458203467526a2 +size 135914 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Cancelled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Cancelled.png index 067840d7c4..adde2c2bd5 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Cancelled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Cancelled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8422e4f58e55fe274ec18161a9444e23aa73adb0466a8b755b3b6a649554e3d -size 54367 +oid sha256:6f1cbd5eefb18e3b9f952506c05b1554fdd21d40b7e006ad6c3771e140cdd4b3 +size 53934 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Connecting.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Connecting.png index 234cd2992d..fe3c7bee57 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Connecting.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Connecting.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21a30d5a2f1867017e9c103f5209d44e0711a99da42a0af50684ef4186b1f415 -size 49625 +oid sha256:40c3d7ce3f32a98c0aabb13f807142330e1874d41a0bf63b5709782398a92b71 +size 49023 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Connection-not-secure.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Connection-not-secure.png index d9875bf827..e465712048 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Connection-not-secure.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Connection-not-secure.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:195ec7a7acb8d36b6533bb0767cb15f1f73a70f1b33be3cc594ebba6b0827b00 -size 108169 +oid sha256:7a7c7802476261e350ec1ea2b5b25b92f0482658983da5b5b689faac232d0064 +size 107679 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Declined.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Declined.png index 2a6eab3c1f..4fff6be402 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Declined.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Declined.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cf0733f9cb5b866aa2b6ee54c7f331b6b9b773090390e711042ec595f0482152 -size 50417 +oid sha256:a38d678825ef3fea61566f453e5b917748e1a822de0c5d2537813e60762bba16 +size 49901 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Device-code.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Device-code.png index f94d4567a7..707ba4c0ed 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Device-code.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Device-code.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c4831d09c7edcaa594b5801981c165458305058df76e59e7096dddf2db0730a -size 69984 +oid sha256:892a4fecc34e5841921e17d7be8b3913bf4d92211584594826b7b99bcf37530f +size 69660 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Device-not-signed-in.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Device-not-signed-in.png index dbccda5399..85dc74d3eb 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Device-not-signed-in.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Device-not-signed-in.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e9588ee9d8e114b4b9545c8517f12704697151d1dd93479d7b84dcbf3fc6597 -size 67181 +oid sha256:60729a9b57e9eaa3097ea34ab5e69d6a8430c23fad744ff6603eb0311b15ad76 +size 66737 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Device-not-supported.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Device-not-supported.png index d96f578985..aa8eada123 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Device-not-supported.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Device-not-supported.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:71d809d53eb2640958455bef5fcd47fbd0e91e03da7f4277735a36466ec04fb2 -size 53685 +oid sha256:6c93e78aaafc01a1edf17efdd111997143d91b43ee56f156f2419339e16b7556 +size 53281 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Expired.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Expired.png index f27c7926e5..ac28a73204 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Expired.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Expired.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4dda377b30757f5414a4a769b9b86dbfed3b24eaf73b3299228ec2b6ba5df02b -size 55024 +oid sha256:d15fc14d286facdee39a061d9c0fb320a4afcf3e17bb7b53e58ee56fa976aa20 +size 54678 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Initial.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Initial.png index dd30f177ea..0a4e391acc 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Initial.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Initial.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf8cc70bb5b9a1a79ca0743de1ff11521aa1ae81af1730a6d4988f77e14fe38c -size 92959 +oid sha256:795d23135ed05cb33aefb8e11caaf26adc8251c5fd912b571b15f56ff5eb707a +size 92756 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Invalid.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Invalid.png index 5e80eba109..7badac8ede 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Invalid.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Invalid.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fad22eb121804e40176bde27d4ea74dbc3303fecac2257cbe02032561f260b65 -size 60411 +oid sha256:5b3c164461061d5b307906370626895692db345f0f8de2264d8bffe271a68796 +size 59833 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Linking-unsupported.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Linking-unsupported.png index 19d5717930..c0d7d304a1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Linking-unsupported.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Linking-unsupported.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b955f7544eb45553fb404510701f71e508ddf07fc23e2062c2fd4a67abe5ffc0 -size 73343 +oid sha256:543174a8a76127d5643751ef8d68155cf94640468131f3dcedae029db850a4a1 +size 72795 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.No-Camera-Permission.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.No-Camera-Permission.png index 7da9c67e48..9cfd68d3b1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.No-Camera-Permission.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.No-Camera-Permission.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11c72bf913329e68875c056a2e1397baed7a42e4afd964331029db5db5a498a9 -size 69608 +oid sha256:c2590be759ce492cc5527e04dee8ee501c277b288ed7446423a3924f8b3283b0 +size 69189 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Scanning.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Scanning.png index 0c00b83cd7..0b2fa4ff68 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Scanning.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Scanning.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8cd077e86fdbaf5b2d499ec3bb914689a62e32a806ba8dfdc72bc2593faf9cbc -size 43906 +oid sha256:6038a31bbc6e1ad7a6d69fd97213f8db6c7f8e836bf14a0f097d0f43c807c407 +size 43325 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Unknown-error.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Unknown-error.png index 5a95e82a85..068a7b9b7a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Unknown-error.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Unknown-error.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ba66c0f1bb81561e51c22a962da53494072cc79cf47206fa1adde520ea9f4ac3 -size 53221 +oid sha256:ee3d502a700e96276bc11f52229c716490d9b1a4fe3b5243632cdca346980a5d +size 53262 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Verification-code.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Verification-code.png index d9f9554334..ba87757a73 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Verification-code.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-en-GB.Verification-code.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4f911552b4aa5118c2ea9660a0c4ccfccd0132558ded80b58760e13a64ed0b6 -size 70163 +oid sha256:81da51e68b9545bf5258859f3482cd268a6bfe0e432a8ebd3006c6b2d393c17c +size 69647 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Cancelled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Cancelled.png index 61595d8983..34dc5cb7c8 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Cancelled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Cancelled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd6abe5f0e70a25e6a51461a3845316823681acf17d1572255401b99ab0b0209 -size 68103 +oid sha256:be3f612e77f5bfd1a1650289b1b78abfa22870adc9c56b7df1c82277bd7b3cda +size 67601 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Connecting.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Connecting.png index 3e98f32214..f20e7a696a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Connecting.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Connecting.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:baf759c20473c39ba45456e5283b63d93501d39748c2f744dd38775a61c67c1e -size 57549 +oid sha256:5c808263ac3e28215953834540e970f3af9b6138d735603096c24751e4b0c44b +size 57158 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Connection-not-secure.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Connection-not-secure.png index dc453e6f24..4ad6c4aac2 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Connection-not-secure.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Connection-not-secure.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:726da5e97cf060ea185394aa68ae1c7c438186662efae37bba2765e49aa64691 -size 163975 +oid sha256:d696e75100c016d520f2dfa2bc84bcea8fa6653a4eacc236eb0bfe78e815254d +size 165619 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Declined.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Declined.png index 1a6e18aaff..8cf5d3e08d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Declined.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Declined.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0407681f7e66d1880b186ac3befaff4d0edb563bb17337dac9b11d4c9c6abc4 -size 60119 +oid sha256:5372c51b29321404e2db215b51bf2d99262a46378c2844fd6120d0e897ce0a3c +size 59499 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Device-code.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Device-code.png index 377d1c467d..fcfb105464 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Device-code.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Device-code.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c8af4b3857547ebe5b9bcb9daf121b0465c26c4f6a5c4cfacea72dfd3a24585 -size 91363 +oid sha256:9d77a950d50a4fa6da5a7940989ee267a27a2279d82d3c80b407415c45c5095b +size 91092 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Device-not-signed-in.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Device-not-signed-in.png index ea6fb1c037..30bbce4b19 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Device-not-signed-in.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Device-not-signed-in.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:829fa5bb4501361c617dac261fc7be3f7542d7b84672d9ac6f602d4c6dfc47ab -size 87613 +oid sha256:008f5b30a64f6dc800787f792ee7c8645f181bf46f64604df2649dc84bc340ee +size 87440 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Device-not-supported.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Device-not-supported.png index 74f27cb8bf..19463de3de 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Device-not-supported.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Device-not-supported.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a0242e1846e733e4bc5781abc789b60cfe33533dd29ff61889dfd9fad2f4f90 -size 67677 +oid sha256:2cb9ba8a842f0f07ad0125e3bc7705103a488f334e3d86d16740102fd3a0a04c +size 67263 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Expired.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Expired.png index 7604c48c8a..b90a007b95 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Expired.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Expired.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc08da52b2237bdbf3620b30a76ecadbe52678bd396188d680a7e9df3f90ffe3 -size 69492 +oid sha256:69cd7c6a4ae4818b8eee722e2dfd40e1885333f3c83708f2f683220858bb3677 +size 69009 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Initial.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Initial.png index 5d6b91504a..fa5e41e8a1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Initial.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Initial.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b9334283a4a16c6809f6e692e77aa989ccce35a72eac319e5e28497f2e81fef4 -size 133735 +oid sha256:d35ebb140a4b6ae9f32e3fcd9e2b492efda4708661feb2c88d03437f102b5a28 +size 133491 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Invalid.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Invalid.png index a43e0155dd..cd8ae8770d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Invalid.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Invalid.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fe92e54b674cab9820bf5b34f07c4917f99831c388b317cb8b1b6cfd3521e74 -size 73154 +oid sha256:f135aace7da6e120d9af82f0cdfbc78d1b0cd6116b49ce0e72e0cf1153408c7c +size 72751 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Linking-unsupported.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Linking-unsupported.png index dce8d88ca4..2ef13f0e2a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Linking-unsupported.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Linking-unsupported.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:99255ccefdf1c2b9049da5cec51b2ce6f29a61a58cefd72b23fdfe1e9a7c9a29 -size 102678 +oid sha256:1e7e0c1fdb71247a02d023f12245951351f3689063c8841268191a3f87343243 +size 101989 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.No-Camera-Permission.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.No-Camera-Permission.png index 5cd014e504..affe2b7f4e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.No-Camera-Permission.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.No-Camera-Permission.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98eeb14b6c42ee7c711e2e2438e498ea460d9ee6bb72eed58590d67eb371297c -size 96407 +oid sha256:6994b86e23b87c551d271a98e9d7b55ebd9ed3d5630eff42c004931e0edec742 +size 96000 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Scanning.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Scanning.png index 1e02d9c54d..a14186e98c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Scanning.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Scanning.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:225dfc6d18465fd502057cf09829440df9399690314d0fb3fb1041f668b0df3c -size 48734 +oid sha256:b8330e6ebf5af84743c544bd05f553f9518342a9e57e05fac29e248227a35b41 +size 48285 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Unknown-error.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Unknown-error.png index 75da7a1b17..2d283b1f51 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Unknown-error.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Unknown-error.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6817942df8d692dbcbf018ccd369a416691ea12bfbd0af990b86c66be8d6dc4 -size 66715 +oid sha256:810d777c450842859703bf3c997a3e6baa40bf9319390daa4f80115c87ef2414 +size 66560 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Verification-code.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Verification-code.png index f246f2fd83..9ad3f715fb 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Verification-code.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_qRCodeLoginScreen-iPhone-16-pseudo.Verification-code.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0043994a4a6d30673de03f27825bdddd76434e252379e8b12b4e287cd8932529 -size 92738 +oid sha256:78f8484ace93d6b3e30eee3534682e35a35efb33c87a18615aa5f6d32177fb92 +size 92307 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Identity-Changed.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Identity-Changed.png index 71249ab4eb..30145583a8 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Identity-Changed.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Identity-Changed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af4e44604db966239fc9ca5918e3fb0cf5f9c126a1785c007a4ee7e122270e98 -size 121521 +oid sha256:73b417708aeb5be45accc886319838271792198dfdad783c067631d85244d982 +size 120964 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Own-Unsigned-Device.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Own-Unsigned-Device.png index 77c4ea61a2..5b87c33c03 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Own-Unsigned-Device.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Own-Unsigned-Device.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fbdd56c2c86f9b49eef80d22ae63419be54ebb05a9b4f476b09ad39add633eb -size 125438 +oid sha256:2f79d62b245df73fb4ff18da49825dd662133bc43e0ec72b1638d2d1d348e770 +size 125004 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Unsigned-Device.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Unsigned-Device.png index c7ee66b1f9..73d4a2f5b6 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Unsigned-Device.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-en-GB.Unsigned-Device.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa265a85461f9bbe5b93230b04ed3efb053cb5a5491861c2da6a1680d971c1e1 -size 123492 +oid sha256:5d79c8e134c300d0a4d7479d75306ce578468b6acb78ac78c1fc8cfdcd934562 +size 122997 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Identity-Changed.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Identity-Changed.png index 8e5c835af9..3a7b1c5369 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Identity-Changed.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Identity-Changed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79635a61bf62cdc667612ab43065fa76e2f63accf129b29110fdc5fb8d1b5129 -size 154977 +oid sha256:508da38a03c318357cc54841b9170881796ee88310063aa072f7dd2213f45b30 +size 154477 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Own-Unsigned-Device.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Own-Unsigned-Device.png index ac16d80a94..dc6764bb80 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Own-Unsigned-Device.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Own-Unsigned-Device.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a68411b261462636bc29807e3f6f146294ee0de54fc40f93bca95ac3abe45cbf -size 159605 +oid sha256:71e473e6bbb06927bd3cf6a6c673a83d952142a2db9c273348fd38961b6b0f36 +size 159364 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Unsigned-Device.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Unsigned-Device.png index b8f2e5b2d0..1a031ca8f9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Unsigned-Device.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPad-pseudo.Unsigned-Device.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1c79aca6ec5dbcae46fc48f9785cb420b9ad14432c17b1090ae9ff4240cc24f -size 158716 +oid sha256:6b608c008d3d2900627090f0db9b17fef0c6fee08f04d61fa5c773324ad979bf +size 158503 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Identity-Changed.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Identity-Changed.png index aec3b712fc..6bede3bb44 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Identity-Changed.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Identity-Changed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d6bf687431f2c06a8c9fc7490c6456f4bac88b53cf78a3557bdb50c96ee4c75 -size 85170 +oid sha256:b4fd99ddfac34b5c8b2d78d57b754b397b526c0434823a03355bb56f19ceeec9 +size 84829 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Own-Unsigned-Device.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Own-Unsigned-Device.png index 78979a8d12..52be08a9b9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Own-Unsigned-Device.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Own-Unsigned-Device.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aaae80c0ca61129cf141f4a2ec1c5371ef8e2d69002f3a7177af1f88c824ab1d -size 90001 +oid sha256:a2e3a1e540ec080479a8b666261c8e59a100c086b164f2548730e885d0cf82e5 +size 89636 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Unsigned-Device.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Unsigned-Device.png index a78eef7396..22b07ab7d9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Unsigned-Device.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-en-GB.Unsigned-Device.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eeaebad0964270d0cdce3a25264e316235f0be1b7e61c1d839ead9fc17e34f2c -size 87765 +oid sha256:2cc48dc05280f46524be645c92298ee1e56d1434f292b131f03736e7fea2f9b3 +size 87492 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Identity-Changed.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Identity-Changed.png index 30cdecc698..0915508e0d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Identity-Changed.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Identity-Changed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f1b3131538ad1897d5c6e366736ddda5996178b6975ced6f3d4c60981a08e6d -size 128751 +oid sha256:86e6a401595183c5dfce28928f9ce701df18eae4600e94935a0fe11cb32b1bd1 +size 128216 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Own-Unsigned-Device.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Own-Unsigned-Device.png index 6ea231e2ad..5780d513af 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Own-Unsigned-Device.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Own-Unsigned-Device.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c45bc35fadc6cc5ca270991382a32f0808155234eece265bca19b6717d7ee24 -size 137666 +oid sha256:f667a17a6395680a3f7a9204d5f4d128852a4c738cd634239d36cc597733c9c5 +size 137432 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Unsigned-Device.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Unsigned-Device.png index 0173949776..e446aa736b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Unsigned-Device.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_resolveVerifiedUserSendFailureScreen-iPhone-16-pseudo.Unsigned-Device.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f46bc4eeba947c304fa2d1e91a10e45c0800316c70741ee7a0742d23d782631d -size 133850 +oid sha256:654a01f609e7b125e4f060c45cf191c767f9e3d6997ae24396105cda367f2215 +size 133440 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-en-GB.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-en-GB.Set-up.png index c5b2362eee..5fba7b07ab 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-en-GB.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-en-GB.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a643050cc5942ee578339ebadb7866dbb40bf8f7562f23ab4153713c9c2a0ec4 -size 133317 +oid sha256:0df8343fdaca7ed2ffa9b81b493cb1a5ab896d0f2cef08f83c0721815eb6c59d +size 133003 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-pseudo.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-pseudo.Set-up.png index 49e9d4252b..f61b87852d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-pseudo.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-pseudo.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:854f870b886503f284b57f1504cc10c4d9b26314330858b11f5a6b4c039ff5ae -size 179519 +oid sha256:0511b9ba854183d086141e097e2cb88e39af7225d235b136e23676dde7447e9b +size 179038 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-en-GB.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-en-GB.Set-up.png index a88c53b622..9da5b29f65 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-en-GB.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-en-GB.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:03a2f691e2f6625f0c043dabb390367e25ea4b61632e0d05bb1e313ed2dd3d3f -size 89316 +oid sha256:5e02892489581a9b285085fcda484561ff2f88e552934d6fdf3b78c9e53d8d04 +size 88970 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-pseudo.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-pseudo.Set-up.png index 59cb407e2f..bd7a7f31a7 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-pseudo.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-pseudo.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94a7ce5da69fbe7bd1ea20900c73eb9cb9927d13ac8b2c004d1d75fa0ed8635b -size 137748 +oid sha256:e9ad705443a5b1fdb27deae753c0fc4ffdddfa24316c6934b1e9ba1c9a2c250a +size 137811 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupLogoutConfirmationScreen-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupLogoutConfirmationScreen-iPad-en-GB.1.png index b28f51195d..9c4c694863 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupLogoutConfirmationScreen-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupLogoutConfirmationScreen-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e916834a4e2dbf63997253df785d6f3fd450236394f59a0e07ff9b53e8cafd40 -size 111869 +oid sha256:c48e13bc19e268d5d6d5b02c3256f8087385b956bb22e9fe5722ac9a0e3c1157 +size 111435 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupLogoutConfirmationScreen-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupLogoutConfirmationScreen-iPad-pseudo.1.png index 607012dd62..9d470326ba 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupLogoutConfirmationScreen-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupLogoutConfirmationScreen-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5db371edb4cfc7927da9fa0c8c538290de10ac459b689a601d91ef1a4f5e1f5a -size 133384 +oid sha256:3c731875d0cd892e8dd170280ab4cfd7558801e52385d98611f64ab162423b83 +size 133273 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupLogoutConfirmationScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupLogoutConfirmationScreen-iPhone-16-en-GB.1.png index 555ae995e9..f06fc4c80b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupLogoutConfirmationScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupLogoutConfirmationScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b1bd08cf627c9323e6d6d720e7a39e07c0e473d134d178a25b5ffd5b4f30869f -size 69383 +oid sha256:66d5afcdd7a46bc579b2fb45aa6749bae215757c113a94b49ee3fe09682b0c6d +size 69062 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupLogoutConfirmationScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupLogoutConfirmationScreen-iPhone-16-pseudo.1.png index 5fa2c10906..93d823145e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupLogoutConfirmationScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupLogoutConfirmationScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3665a04b7eb4ad5b90f30f0f288c65d67dee1c8104f5b5853c8d54b27bb9d66b -size 93064 +oid sha256:37fb145a588cb231eedf05e37010e04b646d6c728a0b4dda28881e5d6bdf0aa0 +size 92686 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Incomplete.png index 5ffaca6543..70243b15fd 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ba23d702ec00bf2e78b2b8a5ffa65231afd4447198249e68febbd0cb8b6b85ec -size 107062 +oid sha256:18b21094daf27f491931edd1113b76f188c99f3558271208195792db24eea2ad +size 105484 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Not-set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Not-set-up.png index 8a4b6adb8a..9b699b01eb 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Not-set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Not-set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5b245cd8374447a696422108e5955dc9d426933652304a9fe1fe67bff356c682 -size 126623 +oid sha256:13771e53bc8396d5ad1af3119b78c031f0aa64fcc015e72e45c1e2b712846fd2 +size 125802 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Set-up.png index 6c5e54e17f..17e146e141 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5dbc11892838b9116bb678e1e824a6f4c72ac8ce1df97fb549befb83ebb9e925 -size 125669 +oid sha256:61ec65771fe434a6c34ea47e015ce32f59521c5c86389c0e3989affc155c7c42 +size 124896 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Unknown.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Unknown.png index d5c02e0299..fa0f39a05a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Unknown.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Unknown.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d69adc85173a9dd6bd318fec6c1a713af5d2026fc377b281a34c7a1b9becca1b -size 79419 +oid sha256:f0bdd1681f7a87bae31a601bcf4a2e0df62730a8532169505b2aabab3ab6e982 +size 78759 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Incomplete.png index 1a9cf0a09d..3078c007d5 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1d824857d656ee6768664a0b196b3baacf2c8422a5480f5df8ac9a50af93115b -size 122503 +oid sha256:1bc60a89d5aede42baa7c36eafafe88f326712713551c4c738cf8d0cf3a7860c +size 121893 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Not-set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Not-set-up.png index 4968f59f46..06f510d612 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Not-set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Not-set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e837eaba5212626e18df37ff744cefc76ed03668a4e4ceea5f245f6c171bf440 -size 157836 +oid sha256:8756534bad47d59f637ed3ed5cdde23bd88c58748e6f44df2edf8b4d325e3c27 +size 157434 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Set-up.png index 4354594c38..30033bcaa1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5a757353f75f4489f6a9302bc200bb603dcf9e11944701485d0c90fd92554977 -size 152661 +oid sha256:2b4ceabdf84f86616791951043844e9c618b56b074f16adc97d95bf7418a0d3a +size 151871 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Unknown.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Unknown.png index 072bc09d94..7b3e79dabd 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Unknown.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Unknown.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a18487a803b68bc729390c0dc924b562b564c148582c60dca9603f051ce3f0b3 -size 81568 +oid sha256:625728b535938291ee7433c2e42c356f5984d59ec9c564b7ec114915cc604f30 +size 80957 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Incomplete.png index 8d1a0e2f90..20c8a315f9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d16d499cb8f81e67e7e4fe001bf29a139494f62f6771237c8165953ff9b7beb -size 61510 +oid sha256:60ea8f1f8c02fc5f820a3b41fa4fe411124fdfb73c2fd51e771c51673becfbfd +size 61760 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Not-set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Not-set-up.png index c2d83919bf..65b9e9b4cb 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Not-set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Not-set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9e7bcfd9f86f6c86d4226d525f8eff9e506672d4156a764114ca88836c6fbc02 -size 83033 +oid sha256:15ab57d4af84466b82204b0dbb737e38d2c705ccfade6371494842108458b1bd +size 82439 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Set-up.png index aada5853f3..6f2bc885ab 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:37ccb4c8caca32f36f953699f8ca75b67745cce7fc6d5e893ca8ff9ad8eb0ef4 -size 80210 +oid sha256:cf7b9fec4e99a3d71f9f9f704608f8209239ee1d0a2f89144a720be12bb1f433 +size 80121 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Unknown.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Unknown.png index 57cdc377b9..8a94879843 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Unknown.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Unknown.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4dddadde82de000eea2fcccc46c35157367d032a8da305b8add53fbc70a488b -size 38029 +oid sha256:a22acba2b438aeee6a26703ffafd346972c5ac764834a27b0542fed65298109d +size 37602 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Incomplete.png index 0a23a94084..562b54e734 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df53744db3f78f32a376d79e2907a9c98cf265abb62ac86ec457589e66b4b6b8 -size 81702 +oid sha256:d67b8defaedb1ebd72d37fe40b52f70005cc1104be0e0d67a703c56fe337f98f +size 81282 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Not-set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Not-set-up.png index 5f3deee449..9a5e34fb3d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Not-set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Not-set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:570a805dc39bce8695eccaa7d5a8457f253d6a6893786e175286be0c518958d2 -size 118758 +oid sha256:a56bda1df5ee99fda29529826a288747634a1922b4bc4bcf544fd86a9e2097fc +size 118514 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Set-up.png index c53f8b8bcc..541fb03459 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c663002eef07dbf37887eb07478b1e2111b093e47168dde2ecd28bb4d3c0771 -size 114821 +oid sha256:3c10cc0a97a467368cac68fac0c2a67f1a68050e755349c7b008ba1bd7098853 +size 114487 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Unknown.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Unknown.png index 2e1619ceb6..2d9df0b7f1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Unknown.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Unknown.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31169fb944e6abbad9b3f15c3faa0e0d51ff32ab1641490c42358bde08dc5927 -size 42859 +oid sha256:6122ab324601debec8c3374c937852d0fef80951ad3771fa06f8b8db914bef18 +size 42478 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPad-en-GB.Login.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPad-en-GB.Login.png index 63b63b38e9..14a90fe890 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPad-en-GB.Login.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPad-en-GB.Login.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69b7c02c864575e3e69cbf8bbc727f6124d06841c3540e33dc523b84d3bd3b73 -size 103197 +oid sha256:dda96a2b756c7c250b110dc89193dc510880503a766ccbb7a00cc42f36b4959e +size 102561 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPad-en-GB.Register.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPad-en-GB.Register.png index 163e0a3edc..317528e0fd 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPad-en-GB.Register.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPad-en-GB.Register.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:663437838152f1bed1aa7f29cee3233149db54365997558fd585a5ae118187dd -size 108895 +oid sha256:3e82342e60533c240a9bccfdcec4ba0fad0fad2c458191d4a0bdf55eeb956265 +size 108381 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPad-pseudo.Login.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPad-pseudo.Login.png index e4a873d6f1..2544aa36e5 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPad-pseudo.Login.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPad-pseudo.Login.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:957cdcbfcd9b91532e3a739039ea026dc4d7bb1e4dc79087baae5da22abb07cc -size 120581 +oid sha256:5d3ba91d70369f65259c8200e796c3de3fad1a290740aa82e36bff89a0e45565 +size 119906 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPad-pseudo.Register.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPad-pseudo.Register.png index fb2350f2c5..fb32c8e31a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPad-pseudo.Register.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPad-pseudo.Register.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:65620857fe08f2bb1afca7d221e892c1ace29a3f5d783554b45e8d6b804ae097 -size 133759 +oid sha256:a52862914c8f4e530cc270e488b8e59e5e9e20ce1fc794b94c5c32da560fa282 +size 133280 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPhone-16-en-GB.Login.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPhone-16-en-GB.Login.png index 14ee51d788..9facbc8195 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPhone-16-en-GB.Login.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPhone-16-en-GB.Login.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e4a361eb442339296e27e0999269fa8e248035e58a40483b3884f9ca4d145f4 -size 63625 +oid sha256:2ed8e64bec27b38226b9686e62352c41804e797b170740effc37dbc5d065903b +size 62986 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPhone-16-en-GB.Register.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPhone-16-en-GB.Register.png index bf678fe702..0d46ece573 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPhone-16-en-GB.Register.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPhone-16-en-GB.Register.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:464018bac08f003b3ce9aa32bac694de3b0ad88ed879aa983b38c3fa4609e2d4 -size 68720 +oid sha256:267ebd6d4603e9fc6e247bc710bda29c20a7c4b14826dd6eee909efde4f26de9 +size 68125 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPhone-16-pseudo.Login.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPhone-16-pseudo.Login.png index fbac181cdb..a4dbaca01e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPhone-16-pseudo.Login.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPhone-16-pseudo.Login.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:18bb05ed7f9e9d2d6b0261cef8d6f16f86c0930b0b699e8b5ba3f4ef9d50a84f -size 87593 +oid sha256:5e5152dd16d06c592d0f6f2181790da2f0d06814c4efd36a70bff9712e99e7a5 +size 86985 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPhone-16-pseudo.Register.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPhone-16-pseudo.Register.png index c60f8acd60..e22a809329 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPhone-16-pseudo.Register.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverConfirmationScreen-iPhone-16-pseudo.Register.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:65e733035181c72339220c3ead9e272102bf0b6f1e5667d90fa766e82a5655d5 -size 98319 +oid sha256:10a15b4ce667f2b90974ab62a8b2dac53cbffd6e6158d2b564bf42678169eb0e +size 97699 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-en-GB.1.png index f080e1d32a..90ba98db03 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:439d4c6e81324776c8608a96981a2d6056cf8ea148fcb7d11d4102be3cb84226 -size 119879 +oid sha256:e8b491ed0dec54d7a2f3a584b077b8ad7cf2754a1bf2d330056740b3a49bca12 +size 119704 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-en-GB.2.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-en-GB.2.png index a30d966e22..f96dd6020d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-en-GB.2.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-en-GB.2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1334150c61f1c2668c7c1e11f3837ffc7c8589bcade6a3069d7370972079b0de -size 116778 +oid sha256:1533313c12b1a46a77ea33b1dee9a3f350df3103f0b335d9874c032abfd58b3c +size 116735 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-en-GB.3.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-en-GB.3.png index 49b86131c9..87c5f78675 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-en-GB.3.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-en-GB.3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b24a1036fe61c64214c929d53d409723bf388191205d4392759009680b451ad3 -size 121094 +oid sha256:19589d86321fa6347cce9a2dd229915e3c154d0544b6755bb4616ed11aec11d5 +size 120953 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-pseudo.1.png index daf436d890..0ee4bea59f 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:33d9e3a0e09db01227458fca2f193694209a3efb430300a3445c98b9365cda7a -size 140243 +oid sha256:24347f1d5e2940457895544373800ecb5fa90adf742c007cd3ec949b8b5481e6 +size 140253 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-pseudo.2.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-pseudo.2.png index b62b7e9bf5..b6c54a7baa 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-pseudo.2.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-pseudo.2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30b119fc0f21681cd6cbe746044f8cf8681437f1187053b625c75ceac146b3e2 -size 137967 +oid sha256:6fedf30ec4ccf247e3271cf483e95e2db7ea9c8c2cd6229d64bd730a5f269ec0 +size 137968 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-pseudo.3.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-pseudo.3.png index 8014ba2261..6abe1c16ec 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-pseudo.3.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPad-pseudo.3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8048d1dd4b99792cd4c68567499cfe3aba40a4e3b0697eda3d1b67a115d684f6 -size 145736 +oid sha256:0b59507030a14739ae053cabff10de7ecc336c9e68815a9a2889f881f4535415 +size 145823 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-en-GB.1.png index fc2b05231b..810cf610d6 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11a03e55323cfe1235ff9cdf14dfb0ff39299dce79dcac4a51201a97713a3472 -size 71503 +oid sha256:ba29b8a0d4484e5863850bbb8c5b00a4248f98bc6c5e74f09e71635e0e90ca48 +size 71280 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-en-GB.2.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-en-GB.2.png index bc8dddb12b..35b13faa22 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-en-GB.2.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-en-GB.2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae50c7000f5aa1648187b20f2e0c9923e390d4b42d0dc080a5c70d58e27cda3c -size 69166 +oid sha256:c5386bd512f1f8e90328b1e50bf960debafb4362278ea64eef04ad34546a68ca +size 68967 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-en-GB.3.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-en-GB.3.png index 26e485e145..78ca3fc5c7 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-en-GB.3.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-en-GB.3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d6b890b433313f7398c38f940ba1db616b89c563c5ba35e6378b0a4863b9c6b5 -size 75379 +oid sha256:b7337fba28d597a810e92e6d593d635dee1ed4eb8c0bc5eb3e6fd9dc30d30626 +size 75140 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-pseudo.1.png index 79e59aedc8..b8aaa041ff 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:85c82642698ac067a6301673053486de0db05f6adf4039f52306d6a221f57786 -size 96116 +oid sha256:c49d85e92d835667b8ec14ca33a48327af2d7523c1c5f234dda40fb3db43e1f0 +size 95613 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-pseudo.2.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-pseudo.2.png index 16d948e96f..2604629caf 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-pseudo.2.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-pseudo.2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e46ccc550d2c0d74655be4a3cff958e62d8e5c8d9f789cf81b31c97b376cad0c -size 93809 +oid sha256:228b9c829fdbd200775c90175db63173686d68ad100a678a60bf3636508c50fa +size 93439 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-pseudo.3.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-pseudo.3.png index 45f0a77eed..088f7036b0 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-pseudo.3.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_serverSelection-iPhone-16-pseudo.3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1fe38dada4e2201f011443eda61791124e33e5423a049ea4f9b7c942c3405b1b -size 103829 +oid sha256:01b30b881e41dc1646a8c2870898bcf4dc54ed61afd7b6a5b06315562c9b7bca +size 103292 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Cancelled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Cancelled.png index 7c73b1805e..56240ba36d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Cancelled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Cancelled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac1201a329e103496c67f7009b251901396831336061ae67cbf086ce1b808836 -size 94156 +oid sha256:94e76332e1728aac71a9684ee484eae180e84c88ea616798df0c5d883ac1e644 +size 93529 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Initial.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Initial.png index 59a9fe8cd5..bd2dd65ff4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Initial.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Initial.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:01967ad4554d2ed30b3927c9d2eae7768254b0103e4b8e39a2cdb64693391798 -size 96021 +oid sha256:7da5f266ee5cfcf31a53a3edd37e6077e9cc5266bdc02009eb69743d69c98d83 +size 95443 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Request-Accepted.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Request-Accepted.png index 17a218ae94..8fd5a7453b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Request-Accepted.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Request-Accepted.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:39c7baba15007785521fc44cee49530a6d11ce9f528f34d09c3976e4ef6a8e7e -size 93367 +oid sha256:c17fe42adcc844769eebfef93482366a692ed8e8d7cd1bd098106e4435e25e92 +size 92605 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Requesting-Verification.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Requesting-Verification.png index 6d4290ff1e..ac07559283 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Requesting-Verification.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Requesting-Verification.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c436e4722a30ab053682f23e0f9c1f3c01d0a12ae14abcfd3f79c61d66135ef4 -size 95485 +oid sha256:6a87fcdb4512157795aeaf16e9a94cab6a18677687f0015edbc0a696fc8a5215 +size 95084 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Showing-Challenge.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Showing-Challenge.png index 78d44f4bb1..79a6ef1996 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Showing-Challenge.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Showing-Challenge.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ddf03ee7b6c5909e9b69726a715a5ee957ec2bc880d6973c45c02bf9b36e829 -size 166279 +oid sha256:90f39070e7c681d4bce49d494fe4cd967092f68dd16767db450d275993ded1cc +size 165443 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Verified.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Verified.png index fc83a0f239..b069b75c29 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Verified.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Verified.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:39c8ee9534c65c7e4bd7eb52e70f9ceb0acef78cbcb49681806c1e9561c949b8 -size 95052 +oid sha256:635a1ce925fbb458b9fe414d6621f78d42d1c00c8ac55c9136b7aa4bbe5315b5 +size 94303 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Cancelled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Cancelled.png index 2625cdfda7..ac4b4d3867 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Cancelled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Cancelled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a0b0b7e7c459b9dac34c6f691339dbfac5ed79a25a7b7747c3f12545b7a9cee -size 106186 +oid sha256:ec94f9759e1a8f8bba3feb88d0e35fceff8f4bd69b3860034cfd3596310f02cc +size 105774 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Initial.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Initial.png index 974525d8ec..42fc1b2e3e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Initial.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Initial.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74d47dc2ad017563d38d66ead6b45a0ac77fc18998a9618bd61f2f3eabe83f45 -size 107167 +oid sha256:2145bd9b612e25b80915d4d5a125026bdb800f505c4fbb7908438da715e79fa5 +size 106608 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Request-Accepted.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Request-Accepted.png index 3993cdefad..f5cb9c384a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Request-Accepted.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Request-Accepted.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e49593329ca0212376e5f2122e1a27f8285771ee0276cb7eae93c77944d7964b -size 103153 +oid sha256:34e080a55f1ba530db20813f64a561439ef668094fb83742ea94f25984385069 +size 102546 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Requesting-Verification.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Requesting-Verification.png index 4bb8853a17..54c3f0007e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Requesting-Verification.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Requesting-Verification.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7def3357b602031d5d231ad8d4924bbd69f7216a5a02a9bbcef37de5b62416e4 -size 106801 +oid sha256:802b547e9461a061bc51df508d8474d59d0175f35da057469dbfa2e8b18414ae +size 106580 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Showing-Challenge.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Showing-Challenge.png index 9f19e226c5..a4f937942c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Showing-Challenge.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Showing-Challenge.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:62e03b414dc70f9319472f47d3ca14d336e858494a9f582632508ddbd335840d -size 185548 +oid sha256:9b083b28e3dd51259f7fcae01f7bd482c684b3458d30dc464aba0d4cc4094b51 +size 184847 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Verified.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Verified.png index 280d01e161..1acb67e6e9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Verified.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Verified.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89a7978637d296f963f82cfc48314f5b6092e8c7296a0b7925f8e0bcf73bc6ea -size 111453 +oid sha256:8c148a7440eb8cba1f937f385949dd60b2aeec5f5e61725d768c524599d27a5e +size 110794 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Cancelled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Cancelled.png index f806c1312e..8f36925c0d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Cancelled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Cancelled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49032353a8a50c869d3da4020a77ae92b4d765d6860b530278a253f9ca2eccaa -size 52925 +oid sha256:40738872c62954bd5f86d4241f3128472c8328c3295fd3f6f46bfff2c38717f4 +size 52459 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Initial.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Initial.png index 6633f8ea2f..f30541b374 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Initial.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Initial.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:77f868bc6778c78e6dbb6413ee22916ede3f537e98630965db2ae0a0d209d53a -size 53700 +oid sha256:7b6f32135cf4cfcbd8fd2414e45c88eff842789c4f5a72128ba76e969c0c25ed +size 53430 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Request-Accepted.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Request-Accepted.png index 0873c99b14..2784a60391 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Request-Accepted.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Request-Accepted.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:261b849a3566352f91a468573264ddabdb66468e9871e41ff99e9b9f70a6c30d -size 50999 +oid sha256:fc443e3c292fc373eeabd6c6194e62b30bdb3aaf62be51c4c6a00f24eecd2701 +size 50313 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Requesting-Verification.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Requesting-Verification.png index 2884cc4d66..683e7ef272 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Requesting-Verification.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Requesting-Verification.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f3c8494d96db0197bdd4f01cbc4c68b3e5204620e458f54aff02a36d51557320 -size 52301 +oid sha256:2b796634f5f0071e70fadfa6b13df5cc06425a42b450af365e91a4e14c0b6636 +size 52007 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Showing-Challenge.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Showing-Challenge.png index ff52cfa4b2..8c0cbbd04e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Showing-Challenge.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Showing-Challenge.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:871011b7284f48f2812dd183eb700dc995380240c5241d3f5aa43e0380e47423 -size 116067 +oid sha256:71eabdfd2d5cc1a86eb42043d1aa5e2a1c401722ecacdab7fd70e45898051c2c +size 114898 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Verified.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Verified.png index 2178a70749..7afd9890d0 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Verified.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Verified.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b9ea2ac42097c75194b08a1f795c5f60b152e5670d6c7610733f7fa49fe28e05 -size 52229 +oid sha256:e9fff8ecc6a113ae1e4ede8a1e599972f47e80bd057b22145f361e8927354d59 +size 51657 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Cancelled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Cancelled.png index 73c478afe2..d56b1dde99 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Cancelled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Cancelled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b14189fe0da611b4645490765a6b57b4d7baf890aaa40e18f46e730696b1ce71 -size 71794 +oid sha256:1c6060f6ce1cd9d447f9d642dd2a007f1ddea9d56b877a567691f885e742e5aa +size 71244 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Initial.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Initial.png index c8bc628bb0..66aab7502c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Initial.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Initial.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:572b1717f115f0ba1d93a9a151c39d1bfd37d5b87b1e6047feaeb24077a003e8 -size 70748 +oid sha256:897ebea0b95135d24dbaf686e4fb6d39bd45057adc514ada77508fddd9a4c985 +size 70591 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Request-Accepted.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Request-Accepted.png index 527759301d..f8f6077cc1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Request-Accepted.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Request-Accepted.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f7129fa293bd0f5eee4a09ea79b4a17b77f9ac470c53ce76f7fef94a74bb3c7 -size 61706 +oid sha256:dfc36dd990bf345a9fa23bd388414745f4724bf342586d120c65efafca228e35 +size 60837 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Requesting-Verification.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Requesting-Verification.png index 46e31c462e..644fc5b68b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Requesting-Verification.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Requesting-Verification.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a327aad0beaaa8488651dd03b411ad386820c6c42e7614a19f74736ca23c0a5 -size 69364 +oid sha256:1c1f50bc1cd49c71bed1e48f540497f82fca9e5ae2130114ed2257e42065a9f9 +size 69054 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Showing-Challenge.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Showing-Challenge.png index 1781a539fe..ddf00c2f41 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Showing-Challenge.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Showing-Challenge.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13db2a4db9a73e75b3af09451efed5dd29062a893a10e03627eef4251e08b5e5 -size 136557 +oid sha256:36baf8914073371e25859a9dcd82bd9e461efebc4a8905fea7b28eb1da84ae08 +size 135368 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Verified.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Verified.png index 91d13a14c4..ff6e9cb0ad 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Verified.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Verified.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:239c4a79f7e61376d48e733752a84ef3e2ad91c66155a835094d9148ea75bb27 -size 72167 +oid sha256:a1590c8d73b8f24da7bc06e741b024bf6c4013670cc9f95fadd931157929591c +size 71663 diff --git a/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-0-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-0-iPad-10th-generation-en-GB.UI.png index ad2bbc45cb..8e24d84d47 100644 --- a/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-0-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-0-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1393931862148c41d36d12b95fb9996dfbf699e364bca94bd1a45d4219def66 -size 152836 +oid sha256:3a9088fb4b320bdae5714e27ce37246287314086513751e4e295258110b7ef3a +size 152117 diff --git a/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-0-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-0-iPhone-16-en-GB.UI.png index 53ac9f3c9b..68f9ef1b05 100644 --- a/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-0-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-0-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cecd870c0baffb415d31a5ad1219ed5a6df8cc142d7b3c157848fc849240ac37 -size 96598 +oid sha256:7b84b3822271da60548b5377a15b691f29fe0342bdb1832ec2ca625e063f70fe +size 95862 diff --git a/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-1-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-1-iPad-10th-generation-en-GB.UI.png index 3fbb8f6106..7bd4df4f52 100644 --- a/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-1-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-1-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:62ecd0c78e6534aa5f71776e59bce5f0bfcba86cfb1045703f1d47bdecc3803e -size 152483 +oid sha256:3feacce0433a2c01983cece54991e07f900d4fcd1204293b7fbf086493744749 +size 151761 diff --git a/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-1-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-1-iPhone-16-en-GB.UI.png index cd70ed5e6b..4b543334a0 100644 --- a/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-1-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-1-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ccdf5b9c4d40ee14717215c18b568c0d347f12a8e9e3859375a7a183f4112b9 -size 96270 +oid sha256:69be9ad1028840f0647dd3bdf145792af5c71016083662954800660d9f705618 +size 95557 diff --git a/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-4-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-4-iPad-10th-generation-en-GB.UI.png index ceeb2f2930..fcee351985 100644 --- a/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-4-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-4-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8ae9afa40428e53bbd54664131ac3a0aa8d1e1feb91036f4b3cdc2aecea77f0 -size 173162 +oid sha256:45c4fe3dea366978d7f5f2b2fdec5f4adc2b77eec2d9a06909aa93e0f9bdac98 +size 172282 diff --git a/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-5-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-5-iPad-10th-generation-en-GB.UI.png index b94c3f891a..091c79e6fc 100644 --- a/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-5-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/appLockSetupFlow-5-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:80764196f842976b1d05f46ce9274c289931cdd5c7b7ca23a53557201a2bd28d -size 172766 +oid sha256:ca28b102ed02b26d6136f28165d5859879a8def084c3e9312dfa6c80b63b20e8 +size 171874 diff --git a/UITests/Sources/__Snapshots__/Application/appLockSetupFlowMandatory-0-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/appLockSetupFlowMandatory-0-iPad-10th-generation-en-GB.UI.png index cb8d352abd..d74f5b63ff 100644 --- a/UITests/Sources/__Snapshots__/Application/appLockSetupFlowMandatory-0-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/appLockSetupFlowMandatory-0-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:127197150b68b09042e105b726bac64c59260722e8f6d624fd541071e5b68826 -size 90485 +oid sha256:29451cacdf506cf7fcb458bdb9dcc922bded3b655ad35a7334579d0477d92a48 +size 89891 diff --git a/UITests/Sources/__Snapshots__/Application/appLockSetupFlowMandatory-0-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/appLockSetupFlowMandatory-0-iPhone-16-en-GB.UI.png index 10440ee5e2..7062050fad 100644 --- a/UITests/Sources/__Snapshots__/Application/appLockSetupFlowMandatory-0-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/appLockSetupFlowMandatory-0-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9fcbf6d7398bd594a7524a6046075434a70b836ceb1e11249ddcfa6d05f373b -size 93225 +oid sha256:d385efc1e1bc3e2353a172a370e5c8d7c24016af24b657f1528b091593abc000 +size 90772 diff --git a/UITests/Sources/__Snapshots__/Application/appLockSetupFlowMandatory-1-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/appLockSetupFlowMandatory-1-iPad-10th-generation-en-GB.UI.png index 828513de82..7fdcb524ef 100644 --- a/UITests/Sources/__Snapshots__/Application/appLockSetupFlowMandatory-1-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/appLockSetupFlowMandatory-1-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f233c22c1f60434d1ad95172e6d6effea6295dda098e98083802cc5eb51cdda1 -size 90096 +oid sha256:cc04a1b65075e130ea2334746496f1547188b3e84c14608877aa6217f2a2d2d5 +size 89507 diff --git a/UITests/Sources/__Snapshots__/Application/appLockSetupFlowMandatory-1-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/appLockSetupFlowMandatory-1-iPhone-16-en-GB.UI.png index 74ee6c6b82..6fb1ea53bf 100644 --- a/UITests/Sources/__Snapshots__/Application/appLockSetupFlowMandatory-1-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/appLockSetupFlowMandatory-1-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:295946af755127d55c16f017001088ec0f8e796b6a5eccd871cf5b8b0155a8c3 -size 92907 +oid sha256:5e28519408070f07c0812f419476c56fe440c8effd9b9e5c491092259a29292b +size 90458 diff --git a/UITests/Sources/__Snapshots__/Application/appLockSetupFlowUnlock-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/appLockSetupFlowUnlock-iPhone-16-en-GB.UI.png index 5c05914977..8d12c66ca9 100644 --- a/UITests/Sources/__Snapshots__/Application/appLockSetupFlowUnlock-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/appLockSetupFlowUnlock-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31d7b840450d86e0bb8d585e901e8eb46673e5cbfd96e765e217b2fcdd5fb37f -size 83315 +oid sha256:79c998e7c0d5ce6fe9eb5c4040a0a59b0a0806a2ab710173db4c4785946076d6 +size 82753 diff --git a/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPad-10th-generation-en-GB.UI.png index 2a869ab2fd..0530823155 100644 --- a/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e6ff8c6a27cc995574326c6ec7952906170abbda18dad5721388fda88595fb3 -size 167621 +oid sha256:c1cd647578cb93945cc07cbeb2273e99c4a5d39c8e02e9e2d5dfde272d59d60b +size 166908 diff --git a/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPhone-16-en-GB.UI.png index 0dacff06ef..6859f0e427 100644 --- a/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/authenticationFlow-1-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:120cd74cb96ee9bf2615fff542d0d8a688448a91271ce6d50540314cb7795092 -size 254990 +oid sha256:f322df60c0efebd8a72cdde5653ebbb47100cf1afb736749715c9b0bbe55a3e4 +size 266989 diff --git a/UITests/Sources/__Snapshots__/Application/authenticationFlow-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/authenticationFlow-iPad-10th-generation-en-GB.UI.png index cf637a6584..772589b524 100644 --- a/UITests/Sources/__Snapshots__/Application/authenticationFlow-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/authenticationFlow-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a33f016455e67ff35b96397a3d06e27bf808d6134338730f09d4665f051d2a1 -size 85541 +oid sha256:ef5e6b24ab13e84fd763387cdeeb60255d4acd37daf1b34d01c7a706139dff6f +size 84644 diff --git a/UITests/Sources/__Snapshots__/Application/authenticationFlow-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/authenticationFlow-iPhone-16-en-GB.UI.png index 984e8f66f5..14920505f5 100644 --- a/UITests/Sources/__Snapshots__/Application/authenticationFlow-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/authenticationFlow-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec22582ef3f19a2830baa9084507b98b9cfd00fd7c4ddc892a86f533dd921fd0 -size 88406 +oid sha256:7cb6285b10fe064ea7ab09df509044ace622f9c5be87bc6fe59205a0b1f08aef +size 87787 diff --git a/UITests/Sources/__Snapshots__/Application/serverSelection-0-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/serverSelection-0-iPad-10th-generation-en-GB.UI.png index 5f3e3c3c91..dc09fb8c88 100644 --- a/UITests/Sources/__Snapshots__/Application/serverSelection-0-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/serverSelection-0-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2f36787795e1788f4735181d38600ace52be89803b1cd07120b995bfe09bbc3 -size 104074 +oid sha256:56c5eef68d4fbc9ff878efb77940dbdf6d088b117a4902f23c997bac4dc3eeda +size 103993 diff --git a/UITests/Sources/__Snapshots__/Application/serverSelection-0-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/serverSelection-0-iPhone-16-en-GB.UI.png index 411a1c2a19..3e73a3fa90 100644 --- a/UITests/Sources/__Snapshots__/Application/serverSelection-0-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/serverSelection-0-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d9ce2797df74f1901b91ae0369d0bb297b696eaa1275d20c6791da91095c50d -size 114418 +oid sha256:fd188ec0392974182bbc70d28da656ab1805c7923ea6080b798f2abd25c99e4d +size 114316 diff --git a/UITests/Sources/__Snapshots__/Application/serverSelection-1-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/serverSelection-1-iPad-10th-generation-en-GB.UI.png index 15c59af26b..1dae430747 100644 --- a/UITests/Sources/__Snapshots__/Application/serverSelection-1-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/serverSelection-1-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ecffa20de6b440aea0e11b2ca1890e3928b2a2df3593fa03e0a10c70e365fe9 -size 176927 +oid sha256:189edb922c5409a11b14638e7f7dab4fc48e7dd270a256b55f59e00c238812a9 +size 176925 diff --git a/UITests/Sources/__Snapshots__/Application/serverSelection-1-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/serverSelection-1-iPhone-16-en-GB.UI.png index d7f303b0ce..0c134c0319 100644 --- a/UITests/Sources/__Snapshots__/Application/serverSelection-1-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/serverSelection-1-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a6ba2a4bce0e7a5e37f72940947925d677946373e6023c80fa75ff2d2e4de3b -size 177599 +oid sha256:d6fb61933ade921e10beec051261fb7d194bd59e69729345c213a1220c4a0fb1 +size 179444 diff --git a/UITests/Sources/__Snapshots__/Application/serverSelection-2-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/serverSelection-2-iPad-10th-generation-en-GB.UI.png index 74fbb871bc..a10bec9470 100644 --- a/UITests/Sources/__Snapshots__/Application/serverSelection-2-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/serverSelection-2-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6057efab72cf6fee35ae9e188e5a2bee8b4689e766e1679f2a8edf700dc6e257 -size 107265 +oid sha256:fde296f3bd2dc09a59cb74d536adfea8e4ef07fe613e78394af7aaf0bdf202c8 +size 107255 diff --git a/UITests/Sources/__Snapshots__/Application/serverSelection-2-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/serverSelection-2-iPhone-16-en-GB.UI.png index 6d7b8ac9c3..a22acf28aa 100644 --- a/UITests/Sources/__Snapshots__/Application/serverSelection-2-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/serverSelection-2-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d0e481f763f1e9b999ef25380275c46066e9185592d6f83f7b058568c0999a4 -size 120602 +oid sha256:aa40ed612a5ee08b7602fd08203c6984031a489cadd1b70b0b761e2e8b3f6815 +size 120501 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-0-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-0-iPhone-16-en-GB.UI.png index f4d0fe593c..f9d2ed4895 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-0-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-0-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f3a4be3be71f23e0609d26153de249459b791973aba791f932195b5c72229a9e -size 88357 +oid sha256:73dadf138c13037272559f46962e8cbacfc7bf4de07e589a92c6e3d5848a2c93 +size 88038 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-1-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-1-iPhone-16-en-GB.UI.png index 1bd60d5600..5fbf6aa90b 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-1-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-1-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:642585ee55f22565454f2d680a8ff00ca4215915b7d25f539f5e22e72ac3cc14 -size 87888 +oid sha256:3df7a0c44ac8ceeb5509c03f882ccaa57b5bb7fb6d54fa7f7f0ddb2c5be6d6ab +size 87190 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-2-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-2-iPhone-16-en-GB.UI.png index fb85532703..50cbef65e1 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-2-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-2-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8adf508350920b4c5914d487f1f6b344010a815d4effa0f521b7b9231e88a619 -size 84842 +oid sha256:e91b2715da11e04376bf99a910f9b441aa3e21d7fc9e4567b0667c84f8e85b3d +size 83835 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-4-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-4-iPad-10th-generation-en-GB.UI.png index 50e5dafbc0..0f9f266034 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-4-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-4-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9eff84ef4beab29cd3c652f389efeedd787b0910e83c1b2c2c23c7ebf933150d -size 148382 +oid sha256:8d9c8b24cf3351eb186348ac79da58bbd3d54682cb37084b5f923887e8bdf8ed +size 148162 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-4-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-4-iPhone-16-en-GB.UI.png index 257e64262a..50598b817f 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-4-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-4-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bec414e1e15e0a13ba197c8ff3cb8b0a04245749e6402d37ca203074faad4be2 -size 201007 +oid sha256:78ba7b00929594e231aea48863e5224e864717e35a319a573d73af850fed6a08 +size 199952 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-5-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-5-iPad-10th-generation-en-GB.UI.png index 5a41df0546..c0da179fee 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-5-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-5-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea9b96696dae004c420fc922664dd5d88e0190aaab19a25cc29ee2fe0d1526a7 -size 146760 +oid sha256:67c73fd1862c07dc1d93b0e4b98d13703a01b54a268d6352fe7776d0c58376f4 +size 146945 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-5-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-5-iPhone-16-en-GB.UI.png index 6a6cb457d1..2aba6c0bb2 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-5-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-5-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8558ffc0f99d79e64c67cbaa3a52bfbd1e8d6245eb718a4ab0e088bf73fccaa -size 198306 +oid sha256:d3e372a52fe24ebede306c07107f482426b7ae2a8b8ef6c9e6a8a85af433b7a5 +size 197983 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-6-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-6-iPhone-16-en-GB.UI.png index 0bea22494f..aa48c2e651 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-6-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-6-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c6ed19a389cd93db51c4ccbc9f242252fc232170506c7a41b8a4012ead100d73 -size 86847 +oid sha256:63d77933b2bc5d6dafde832081e02db26ce779b1c83d5b18bf664b059a297ad5 +size 85933 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-7-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-7-iPhone-16-en-GB.UI.png index 6428d1d884..b10bda294d 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-7-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-7-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8673367797e25f39869cb3aced087f5a86085f21555457479ec67770e3455853 -size 87841 +oid sha256:0714f4889baa952ec9597be6ab56c5aef38a706ee80bc881fd395d46f4bf958b +size 87063 From f26159a295159332b868523d36b72543adf8211a Mon Sep 17 00:00:00 2001 From: Mauro <34335419+Velin92@users.noreply.github.com> Date: Wed, 23 Oct 2024 18:29:41 +0200 Subject: [PATCH 074/114] update compound, and updated tests (#3440) --- ElementX.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- .../test_createRoom-iPad-en-GB.Create-Public-Room.png | 4 ++-- .../test_createRoom-iPad-en-GB.Create-Room-without-users.png | 4 ++-- .../PreviewTests/test_createRoom-iPad-en-GB.Create-Room.png | 4 ++-- .../test_createRoom-iPad-pseudo.Create-Public-Room.png | 4 ++-- .../test_createRoom-iPad-pseudo.Create-Room-without-users.png | 4 ++-- .../PreviewTests/test_createRoom-iPad-pseudo.Create-Room.png | 4 ++-- .../test_createRoom-iPhone-16-en-GB.Create-Public-Room.png | 4 ++-- ...t_createRoom-iPhone-16-en-GB.Create-Room-without-users.png | 4 ++-- .../test_createRoom-iPhone-16-en-GB.Create-Room.png | 4 ++-- .../test_createRoom-iPhone-16-pseudo.Create-Public-Room.png | 4 ++-- ..._createRoom-iPhone-16-pseudo.Create-Room-without-users.png | 4 ++-- .../test_createRoom-iPhone-16-pseudo.Create-Room.png | 4 ++-- ...omChangePermissionsScreen-iPad-en-GB.Member-moderation.png | 4 ++-- ...hangePermissionsScreen-iPad-en-GB.Messages-and-Content.png | 4 ++-- ...st_roomChangePermissionsScreen-iPad-en-GB.Room-details.png | 4 ++-- ...mChangePermissionsScreen-iPad-pseudo.Member-moderation.png | 4 ++-- ...angePermissionsScreen-iPad-pseudo.Messages-and-Content.png | 4 ++-- ...t_roomChangePermissionsScreen-iPad-pseudo.Room-details.png | 4 ++-- ...ngePermissionsScreen-iPhone-16-en-GB.Member-moderation.png | 4 ++-- ...PermissionsScreen-iPhone-16-en-GB.Messages-and-Content.png | 4 ++-- ...omChangePermissionsScreen-iPhone-16-en-GB.Room-details.png | 4 ++-- ...gePermissionsScreen-iPhone-16-pseudo.Member-moderation.png | 4 ++-- ...ermissionsScreen-iPhone-16-pseudo.Messages-and-Content.png | 4 ++-- ...mChangePermissionsScreen-iPhone-16-pseudo.Room-details.png | 4 ++-- project.yml | 2 +- 27 files changed, 51 insertions(+), 51 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 5d1487ca48..f36c1fb660 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7942,7 +7942,7 @@ repositoryURL = "https://github.com/element-hq/compound-ios"; requirement = { kind = revision; - revision = 4535fa11bba2efec8463a79deb5722b6b487d845; + revision = e3f9665621872f60d3652579c3f0dc7bf806e72c; }; }; F76A08D0EA29A07A54F4EB4D /* XCRemoteSwiftPackageReference "swift-collections" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index c011f4376f..ec71df9429 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -15,7 +15,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/compound-ios", "state" : { - "revision" : "4535fa11bba2efec8463a79deb5722b6b487d845" + "revision" : "e3f9665621872f60d3652579c3f0dc7bf806e72c" } }, { diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Public-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Public-Room.png index 97c0f2fdb3..494741c06c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Public-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Public-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2b6822fdd0ddc3b3c87d0142377a0951e9478c2fb1a1b840204589b2bc6270e -size 175831 +oid sha256:e1c6b345563649b24c3bda6b427c27f10965eb2bf7c0e0a1c7c58db3949d03e1 +size 175943 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Room-without-users.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Room-without-users.png index 040f987ee0..bc1f6a8fe9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Room-without-users.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Room-without-users.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d4472c871f2188a6d276ed4ef82e48b8a135afcc60ec83cbd974dfe98a8b332d -size 147686 +oid sha256:ec0202e335366e132947b3f4d1c97ce82e105acf45f398b9cf2bbe55414588ee +size 147726 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Room.png index d7c08ebcf4..11c561c9fc 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a4c6a7b0eddf99d68deee0202bfbe20d97e8bcd7c2560feb592954db198ee99 -size 147377 +oid sha256:8f4a9dc7137a6a072e2e0d76bdc5e8c22ecdaac32872bee5a6dc0179f19aabf5 +size 147402 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Public-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Public-Room.png index 4a25b1b65f..321e4f6c8f 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Public-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Public-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1b805b55944beb58caccd5dbf4e98261582fe006ce8c08014bfaf26c13eea75f -size 211876 +oid sha256:091968b37da82fb3f2ac667a605dc7336454a5fb96a1d23ba1ffde9c39bec622 +size 211937 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Room-without-users.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Room-without-users.png index 57c4ed90e8..69e54e34a9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Room-without-users.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Room-without-users.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3bfa52c8580af52c0aa96a65cbe848f88ed7232b0a29270b2d7ebc2c769b35a -size 171707 +oid sha256:3dbbb98babb4ce5d4289bdf174c4e621a68be3ad11b4567f42e95d8884e006f1 +size 171700 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Room.png index 180cc761cf..672109113e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b38d6fd62142d45fd83313d5a7af2954bd0b339cb875f36d59cf0e5f0fbb22cb -size 173026 +oid sha256:2d6f0a23bbebc6d474b3e79269d507674016ec47dda0a03c018f660274c8f041 +size 173042 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Public-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Public-Room.png index 2106fa84dd..1a3b80ed0e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Public-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Public-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0fccdc54a2d88e39353bdb49f32040749072dd17ac3c6cacc165ab6f0812d500 -size 123697 +oid sha256:0d3e33cc4aca21c34c9975cfca6e9002579ab4d1c9e73ce6320c4768a47f7f4d +size 123703 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Room-without-users.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Room-without-users.png index c93ed1d395..ffe3ec6871 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Room-without-users.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Room-without-users.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:effcd533de2760ac2def29097a09147cf7d60fa2bd8844088c123dcce9e641a8 -size 99367 +oid sha256:9b8e2e588467837b31e03191cb116f437038dbb5fde756b2f80eeff10fe25ae8 +size 99369 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Room.png index 91834a38f6..7406d01af1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:248815b3152639d27193e7ab2c412c76036ab6d0394c3b89cc827a4bf4634885 -size 100098 +oid sha256:3883d12090f025447534784ee3067ead0abb6a91f7128b2f9616633e64548665 +size 100102 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Public-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Public-Room.png index 712e0347aa..16fc435376 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Public-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Public-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:35ecf1e814eedd5f2d388224aee6fc885b1d9e1fe174b9199d9fe7bb9004a355 -size 164558 +oid sha256:2ba52fb45501af7ba6e80339749c38e6a975fbe58163c62c7ffe23e2eddc3e3b +size 164555 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Room-without-users.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Room-without-users.png index a8f7b5af29..5afeabb87d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Room-without-users.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Room-without-users.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f14434bda971e011a023d6cfcba900be70ee07d583100a9db3bcc5c827cd6b2a -size 138818 +oid sha256:3bedde91879578d668626ee76161ec184854b4e950fbf51b68fc3733debae0b8 +size 138817 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Room.png index 442b2051c7..ec55d698f7 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6908ceac42a93152b2dfe3c4d2781f4f5cb96bfb165bf6896283cc529cd81e03 -size 139491 +oid sha256:db3ba5d41ecb661e54371b24970734e686f5ab7d6bec5150a0b4f8d7b8ada1d3 +size 139505 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-en-GB.Member-moderation.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-en-GB.Member-moderation.png index 333dd906f2..09effbea73 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-en-GB.Member-moderation.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-en-GB.Member-moderation.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:baacb9057ff518db7af67439e7fc89debf425599c19c209a131996c40e1ae359 -size 133463 +oid sha256:0d6d8f2cc5458cad1667a642f1048b5e30010379fad74d3bace8fb1e8d6df0ca +size 133608 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-en-GB.Messages-and-Content.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-en-GB.Messages-and-Content.png index 894cadfce0..1fddd9c214 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-en-GB.Messages-and-Content.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-en-GB.Messages-and-Content.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:46ec065c952eb4e3afc3af7ce8ee345b93aacab3700153a76fed43a7c8782b66 -size 120238 +oid sha256:c32970e5430a601f966dc026851af7c8896559f56caa1fbb9fe591f1e3d69777 +size 120326 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-en-GB.Room-details.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-en-GB.Room-details.png index 3cd424cbb3..588230d5c1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-en-GB.Room-details.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-en-GB.Room-details.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c2154f032a8ffbcbac33f69832671055bb13637232ea12bcfb09b554bb95c398 -size 138563 +oid sha256:ff824cddfb19cd8229e34b75a4a3ff4604aeed346e35ebe4be53f5226dbbce18 +size 138754 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-pseudo.Member-moderation.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-pseudo.Member-moderation.png index 4555c20fce..1879dde39e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-pseudo.Member-moderation.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-pseudo.Member-moderation.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d97086fdd2914b42f6e70a1741fe1cadf29e151db976128e2371a3e4b1502a6a -size 144048 +oid sha256:b6a86391e0e24bbf3b5bc6e43cc104de3c0a257a683ac42eb9285a4e9e178666 +size 144197 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-pseudo.Messages-and-Content.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-pseudo.Messages-and-Content.png index 2e37e27917..dc9faf3c3a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-pseudo.Messages-and-Content.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-pseudo.Messages-and-Content.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c5a11b430c30cc17b69c340f5fcd4cd35c49c47c02927bb54a6fd6b406c18bf -size 128074 +oid sha256:32333fbc9f13d9d7fadc464c149bf83e7154bf5ffd8f472c2b0d0534b9ef07d6 +size 128169 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-pseudo.Room-details.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-pseudo.Room-details.png index 4be88b0c72..1fadaf9533 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-pseudo.Room-details.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPad-pseudo.Room-details.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:974117a157ffc697c369902c6e4b7ca37de6f78638e14c64449dcdb73b22d62c -size 153671 +oid sha256:5d18bf629121a10c2564fb84dec80beae3dee4e9ba662c494778db3f3a62e8e9 +size 153829 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-en-GB.Member-moderation.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-en-GB.Member-moderation.png index 6977fc0a90..f7b1fd3e6b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-en-GB.Member-moderation.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-en-GB.Member-moderation.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9334d0273bfd78a7ec5e75067eba45ae443cba83f58859cbffbc02c7a3a2a39e -size 80837 +oid sha256:52c7361dcb14a4c259334254b4a45216a5d552817ca8ef421d9692a59dabfc7f +size 80996 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-en-GB.Messages-and-Content.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-en-GB.Messages-and-Content.png index d7871da462..4db5285351 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-en-GB.Messages-and-Content.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-en-GB.Messages-and-Content.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4715abe6c09498294ba2e7a413a080809f166ef0da42c5bd3be2deb1dd1ab11 -size 69489 +oid sha256:c9de739fc8e8b19077906f2859360521936738937c0b5aa060ad415b1ab09e5f +size 69600 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-en-GB.Room-details.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-en-GB.Room-details.png index 4dda036f2f..4ba497c293 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-en-GB.Room-details.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-en-GB.Room-details.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e42a536d87d54d944c68a97f3c845d8d978055aaf684c523c431caf951119bdd -size 85586 +oid sha256:f099aa53aa14bed1960ae2f20792611a10782b58cceefbeb07efa5d7223d3f04 +size 85759 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-pseudo.Member-moderation.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-pseudo.Member-moderation.png index 5661958632..7ece495c2c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-pseudo.Member-moderation.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-pseudo.Member-moderation.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:97839dd8b0691ee4c806d8e08d856f81d05edc9da5a36c784d06685f89efe53a -size 101917 +oid sha256:5827bfd5fbed1c2776b2825141154280b90040ce6273c04fd7cc39d0d388643a +size 102043 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-pseudo.Messages-and-Content.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-pseudo.Messages-and-Content.png index f1990cc897..3c424856a2 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-pseudo.Messages-and-Content.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-pseudo.Messages-and-Content.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1abea40f4acc7eb23e4ba5d926f61de3c0e8a332bd561e85f1f8ed6cf45f0e5 -size 84127 +oid sha256:f7d5df6d760c0cb0680f420c5d8175020df93605358dbc90770ee1285940eea8 +size 84219 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-pseudo.Room-details.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-pseudo.Room-details.png index ab624c0c06..893af37bbe 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-pseudo.Room-details.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomChangePermissionsScreen-iPhone-16-pseudo.Room-details.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a599243be1587e7eba59e715827ffb50f4fd669af4be8c81d8b210a45f5e3976 -size 107742 +oid sha256:20a8af13c9a547f2766d390b82328494092e26708b457beb1df6c731f739f923 +size 107844 diff --git a/project.yml b/project.yml index 9614142e27..250bbe97ca 100644 --- a/project.yml +++ b/project.yml @@ -64,7 +64,7 @@ packages: # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios - revision: 4535fa11bba2efec8463a79deb5722b6b487d845 + revision: e3f9665621872f60d3652579c3f0dc7bf806e72c # path: ../compound-ios AnalyticsEvents: url: https://github.com/matrix-org/matrix-analytics-events From 2b82959430fa476759369e0962dea1f1305f81ea Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:26:53 +0100 Subject: [PATCH 075/114] Fix a bug where the pinned items banner could overlay the composer. (#3441) --- .../Screens/RoomScreen/View/RoomScreen.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift index 72a77e4480..33023ad766 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift @@ -28,6 +28,14 @@ struct RoomScreen: View { var body: some View { timeline .background(Color.compound.bgCanvasDefault.ignoresSafeArea()) + .overlay(alignment: .top) { + Group { + if roomContext.viewState.shouldShowPinnedEventsBanner { + pinnedItemsBanner + } + } + .animation(.elementDefault, value: roomContext.viewState.shouldShowPinnedEventsBanner) + } .safeAreaInset(edge: .bottom, spacing: 0) { VStack(spacing: 0) { RoomScreenFooterView(details: roomContext.viewState.footerDetails, @@ -52,14 +60,6 @@ struct RoomScreen: View { .environment(\.shouldAutomaticallyLoadImages, !timelineContext.viewState.hideTimelineMedia) } } - .overlay(alignment: .top) { - Group { - if roomContext.viewState.shouldShowPinnedEventsBanner { - pinnedItemsBanner - } - } - .animation(.elementDefault, value: roomContext.viewState.shouldShowPinnedEventsBanner) - } .navigationTitle(L10n.screenRoomTitle) // Hidden but used for back button text. .navigationBarTitleDisplayMode(.inline) .navigationBarHidden(isNavigationBarHidden) From 665bdd191fe0164f1f6623d8dc7d117948d5d259 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Thu, 24 Oct 2024 14:04:36 +0300 Subject: [PATCH 076/114] Fix #3369 - Composer mention pills showing up as file icons on first use on iOS 18 --- .../ComposerToolbar/View/MessageComposerTextField.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposerTextField.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposerTextField.swift index a1f73a0011..458e382d9c 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposerTextField.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposerTextField.swift @@ -110,6 +110,11 @@ private struct UITextViewWrapper: UIViewRepresentable { // Remember the selection if only the attributes have changed. let selection = textView.attributedText.string == text.string ? textView.selectedTextRange : nil + // Fixes pill views not loading on the first attempt on iOS 18 + // because the textContainers's superview comes in as nil + // https://github.com/element-hq/element-x-ios/issues/3369 + _ = textView.layoutManager + textView.attributedText = text // Re-apply the default font when setting text for e.g. edits. From cb4d68a75659b2f78b165002423d181ab6c98d06 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Thu, 24 Oct 2024 12:54:52 +0100 Subject: [PATCH 077/114] Fix a bug where the room state wouldn't indicate when a call was in progress. (#3442) * Fix breaking changes on decryption errors and sending failures. * Update SDK and analytics events. * Add extra UTD strings. --------- Co-authored-by: Stefan Ceriu --- ElementX.xcodeproj/project.pbxproj | 4 +- .../xcshareddata/swiftpm/Package.resolved | 12 ++--- .../en.lproj/Localizable.strings | 2 + .../UserSessionFlowCoordinator.swift | 13 +++-- ElementX/Sources/Generated/Strings.swift | 4 ++ .../Mocks/Generated/GeneratedMocks.swift | 48 ++++++++--------- .../Mocks/Generated/SDKGeneratedMocks.swift | 52 +++++++++---------- .../JoinRoomScreenViewModel.swift | 1 + .../EncryptedRoomTimelineView.swift | 6 ++- .../Sources/Services/Client/ClientProxy.swift | 6 +-- .../Services/Client/ClientProxyProtocol.swift | 2 +- .../RoomSummary/RoomEventStringBuilder.swift | 4 +- .../Services/Timeline/TimelineItemProxy.swift | 17 +++--- .../Other/EncryptedRoomTimelineItem.swift | 4 +- .../RoomTimelineItemFactory.swift | 10 +++- project.yml | 4 +- 16 files changed, 108 insertions(+), 81 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index f36c1fb660..b21e08b5b3 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7822,7 +7822,7 @@ repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.0.60; + version = 1.0.61; }; }; 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { @@ -7862,7 +7862,7 @@ repositoryURL = "https://github.com/matrix-org/matrix-analytics-events"; requirement = { kind = upToNextMinorVersion; - minimumVersion = 0.25.0; + minimumVersion = 0.27.0; }; }; C13F55E4518415CB4C278E73 /* XCRemoteSwiftPackageReference "DTCoreText" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index ec71df9429..5e7bb11b63 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,8 +6,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/compound-design-tokens", "state" : { - "revision" : "2af7bb571eb30cbfbd67cdda6617500507ef46aa", - "version" : "1.8.0" + "revision" : "976db67b849775799b4153e7894d61e90fc96888", + "version" : "1.9.0" } }, { @@ -131,8 +131,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/matrix-org/matrix-analytics-events", "state" : { - "revision" : "c9b40120a5f7b8ce1bab3f09f8417fdc9407f006", - "version" : "0.25.0" + "revision" : "9bd3c57e84f87d56b69862369f3b9da714d1d151", + "version" : "0.27.0" } }, { @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/matrix-rust-components-swift", "state" : { - "revision" : "dc3a2199b0e87824ccf06d1207487d2e49c5e584", - "version" : "1.0.60" + "revision" : "2e6378514e79a648d436e8faeb8cd8106910cf0b", + "version" : "1.0.61" } }, { diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 9b9b71021b..6e49740a0e 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -241,6 +241,8 @@ "common.pinned" = "Pinned"; "common.send_to" = "Send to"; "common.you" = "You"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup."; "confirm_recovery_key_banner_title" = "Enter your recovery key"; "crash_detection_dialog_content" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift index ff14107d35..e99fc21f61 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift @@ -59,6 +59,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { /// For testing purposes. var statePublisher: AnyPublisher { stateMachine.statePublisher } + // swiftlint:disable:next function_body_length init(userSession: UserSessionProtocol, navigationRootCoordinator: NavigationRootCoordinator, appLockService: AppLockServiceProtocol, @@ -158,9 +159,15 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { switch info.cause { case .unknown: - analytics.trackError(context: nil, domain: .E2EE, name: .OlmKeysNotSentError, timeToDecryptMillis: timeToDecryptMs) - case .membership: - analytics.trackError(context: nil, domain: .E2EE, name: .HistoricalMessage, timeToDecryptMillis: timeToDecryptMs) + analytics.trackError(context: nil, domain: .E2EE, name: .UnknownError, timeToDecryptMillis: timeToDecryptMs) + case .unknownDevice: + analytics.trackError(context: nil, domain: .E2EE, name: .ExpectedSentByInsecureDevice, timeToDecryptMillis: timeToDecryptMs) + case .unsignedDevice: + analytics.trackError(context: nil, domain: .E2EE, name: .ExpectedSentByInsecureDevice, timeToDecryptMillis: timeToDecryptMs) + case .verificationViolation: + analytics.trackError(context: nil, domain: .E2EE, name: .ExpectedVerificationViolation, timeToDecryptMillis: timeToDecryptMs) + case .sentBeforeWeJoined: + analytics.trackError(context: nil, domain: .E2EE, name: .ExpectedDueToMembership, timeToDecryptMillis: timeToDecryptMs) } } .store(in: &cancellables) diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 0543707297..2439a43468 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -488,8 +488,12 @@ internal enum L10n { internal static var commonTouchIdIos: String { return L10n.tr("Localizable", "common_touch_id_ios") } /// Unable to decrypt internal static var commonUnableToDecrypt: String { return L10n.tr("Localizable", "common_unable_to_decrypt") } + /// Sent from an insecure device + internal static var commonUnableToDecryptInsecureDevice: String { return L10n.tr("Localizable", "common_unable_to_decrypt_insecure_device") } /// You don't have access to this message internal static var commonUnableToDecryptNoAccess: String { return L10n.tr("Localizable", "common_unable_to_decrypt_no_access") } + /// Sender's verified identity has changed + internal static var commonUnableToDecryptVerificationViolation: String { return L10n.tr("Localizable", "common_unable_to_decrypt_verification_violation") } /// Invites couldn't be sent to one or more users. internal static var commonUnableToInviteMessage: String { return L10n.tr("Localizable", "common_unable_to_invite_message") } /// Unable to send invite(s) diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index f5d9d148ff..9755771e0b 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -2837,15 +2837,15 @@ class ClientProxyMock: ClientProxyProtocol { } //MARK: - knockRoom - var knockRoomMessageUnderlyingCallsCount = 0 - var knockRoomMessageCallsCount: Int { + var knockRoomViaMessageUnderlyingCallsCount = 0 + var knockRoomViaMessageCallsCount: Int { get { if Thread.isMainThread { - return knockRoomMessageUnderlyingCallsCount + return knockRoomViaMessageUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = knockRoomMessageUnderlyingCallsCount + returnValue = knockRoomViaMessageUnderlyingCallsCount } return returnValue! @@ -2853,29 +2853,29 @@ class ClientProxyMock: ClientProxyProtocol { } set { if Thread.isMainThread { - knockRoomMessageUnderlyingCallsCount = newValue + knockRoomViaMessageUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - knockRoomMessageUnderlyingCallsCount = newValue + knockRoomViaMessageUnderlyingCallsCount = newValue } } } } - var knockRoomMessageCalled: Bool { - return knockRoomMessageCallsCount > 0 + var knockRoomViaMessageCalled: Bool { + return knockRoomViaMessageCallsCount > 0 } - var knockRoomMessageReceivedArguments: (roomID: String, message: String?)? - var knockRoomMessageReceivedInvocations: [(roomID: String, message: String?)] = [] + var knockRoomViaMessageReceivedArguments: (roomID: String, via: [String], message: String?)? + var knockRoomViaMessageReceivedInvocations: [(roomID: String, via: [String], message: String?)] = [] - var knockRoomMessageUnderlyingReturnValue: Result! - var knockRoomMessageReturnValue: Result! { + var knockRoomViaMessageUnderlyingReturnValue: Result! + var knockRoomViaMessageReturnValue: Result! { get { if Thread.isMainThread { - return knockRoomMessageUnderlyingReturnValue + return knockRoomViaMessageUnderlyingReturnValue } else { var returnValue: Result? = nil DispatchQueue.main.sync { - returnValue = knockRoomMessageUnderlyingReturnValue + returnValue = knockRoomViaMessageUnderlyingReturnValue } return returnValue! @@ -2883,26 +2883,26 @@ class ClientProxyMock: ClientProxyProtocol { } set { if Thread.isMainThread { - knockRoomMessageUnderlyingReturnValue = newValue + knockRoomViaMessageUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - knockRoomMessageUnderlyingReturnValue = newValue + knockRoomViaMessageUnderlyingReturnValue = newValue } } } } - var knockRoomMessageClosure: ((String, String?) async -> Result)? + var knockRoomViaMessageClosure: ((String, [String], String?) async -> Result)? - func knockRoom(_ roomID: String, message: String?) async -> Result { - knockRoomMessageCallsCount += 1 - knockRoomMessageReceivedArguments = (roomID: roomID, message: message) + func knockRoom(_ roomID: String, via: [String], message: String?) async -> Result { + knockRoomViaMessageCallsCount += 1 + knockRoomViaMessageReceivedArguments = (roomID: roomID, via: via, message: message) DispatchQueue.main.async { - self.knockRoomMessageReceivedInvocations.append((roomID: roomID, message: message)) + self.knockRoomViaMessageReceivedInvocations.append((roomID: roomID, via: via, message: message)) } - if let knockRoomMessageClosure = knockRoomMessageClosure { - return await knockRoomMessageClosure(roomID, message) + if let knockRoomViaMessageClosure = knockRoomViaMessageClosure { + return await knockRoomViaMessageClosure(roomID, via, message) } else { - return knockRoomMessageReturnValue + return knockRoomViaMessageReturnValue } } //MARK: - knockRoomAlias diff --git a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift index efa0f5bb26..abc5a60990 100644 --- a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift @@ -2208,16 +2208,16 @@ open class ClientSDKMock: MatrixRustSDK.Client { //MARK: - knock - open var knockRoomIdOrAliasThrowableError: Error? - var knockRoomIdOrAliasUnderlyingCallsCount = 0 - open var knockRoomIdOrAliasCallsCount: Int { + open var knockRoomIdOrAliasReasonServerNamesThrowableError: Error? + var knockRoomIdOrAliasReasonServerNamesUnderlyingCallsCount = 0 + open var knockRoomIdOrAliasReasonServerNamesCallsCount: Int { get { if Thread.isMainThread { - return knockRoomIdOrAliasUnderlyingCallsCount + return knockRoomIdOrAliasReasonServerNamesUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = knockRoomIdOrAliasUnderlyingCallsCount + returnValue = knockRoomIdOrAliasReasonServerNamesUnderlyingCallsCount } return returnValue! @@ -2225,29 +2225,29 @@ open class ClientSDKMock: MatrixRustSDK.Client { } set { if Thread.isMainThread { - knockRoomIdOrAliasUnderlyingCallsCount = newValue + knockRoomIdOrAliasReasonServerNamesUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - knockRoomIdOrAliasUnderlyingCallsCount = newValue + knockRoomIdOrAliasReasonServerNamesUnderlyingCallsCount = newValue } } } } - open var knockRoomIdOrAliasCalled: Bool { - return knockRoomIdOrAliasCallsCount > 0 + open var knockRoomIdOrAliasReasonServerNamesCalled: Bool { + return knockRoomIdOrAliasReasonServerNamesCallsCount > 0 } - open var knockRoomIdOrAliasReceivedRoomIdOrAlias: String? - open var knockRoomIdOrAliasReceivedInvocations: [String] = [] + open var knockRoomIdOrAliasReasonServerNamesReceivedArguments: (roomIdOrAlias: String, reason: String?, serverNames: [String])? + open var knockRoomIdOrAliasReasonServerNamesReceivedInvocations: [(roomIdOrAlias: String, reason: String?, serverNames: [String])] = [] - var knockRoomIdOrAliasUnderlyingReturnValue: Room! - open var knockRoomIdOrAliasReturnValue: Room! { + var knockRoomIdOrAliasReasonServerNamesUnderlyingReturnValue: Room! + open var knockRoomIdOrAliasReasonServerNamesReturnValue: Room! { get { if Thread.isMainThread { - return knockRoomIdOrAliasUnderlyingReturnValue + return knockRoomIdOrAliasReasonServerNamesUnderlyingReturnValue } else { var returnValue: Room? = nil DispatchQueue.main.sync { - returnValue = knockRoomIdOrAliasUnderlyingReturnValue + returnValue = knockRoomIdOrAliasReasonServerNamesUnderlyingReturnValue } return returnValue! @@ -2255,29 +2255,29 @@ open class ClientSDKMock: MatrixRustSDK.Client { } set { if Thread.isMainThread { - knockRoomIdOrAliasUnderlyingReturnValue = newValue + knockRoomIdOrAliasReasonServerNamesUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - knockRoomIdOrAliasUnderlyingReturnValue = newValue + knockRoomIdOrAliasReasonServerNamesUnderlyingReturnValue = newValue } } } } - open var knockRoomIdOrAliasClosure: ((String) async throws -> Room)? + open var knockRoomIdOrAliasReasonServerNamesClosure: ((String, String?, [String]) async throws -> Room)? - open override func knock(roomIdOrAlias: String) async throws -> Room { - if let error = knockRoomIdOrAliasThrowableError { + open override func knock(roomIdOrAlias: String, reason: String?, serverNames: [String]) async throws -> Room { + if let error = knockRoomIdOrAliasReasonServerNamesThrowableError { throw error } - knockRoomIdOrAliasCallsCount += 1 - knockRoomIdOrAliasReceivedRoomIdOrAlias = roomIdOrAlias + knockRoomIdOrAliasReasonServerNamesCallsCount += 1 + knockRoomIdOrAliasReasonServerNamesReceivedArguments = (roomIdOrAlias: roomIdOrAlias, reason: reason, serverNames: serverNames) DispatchQueue.main.async { - self.knockRoomIdOrAliasReceivedInvocations.append(roomIdOrAlias) + self.knockRoomIdOrAliasReasonServerNamesReceivedInvocations.append((roomIdOrAlias: roomIdOrAlias, reason: reason, serverNames: serverNames)) } - if let knockRoomIdOrAliasClosure = knockRoomIdOrAliasClosure { - return try await knockRoomIdOrAliasClosure(roomIdOrAlias) + if let knockRoomIdOrAliasReasonServerNamesClosure = knockRoomIdOrAliasReasonServerNamesClosure { + return try await knockRoomIdOrAliasReasonServerNamesClosure(roomIdOrAlias, reason, serverNames) } else { - return knockRoomIdOrAliasReturnValue + return knockRoomIdOrAliasReasonServerNamesReturnValue } } diff --git a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift index 5088efa29f..74e7a69666 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift @@ -195,6 +195,7 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo } } else { switch await clientProxy.knockRoom(roomID, + via: via, message: state.bindings.knockMessage.isBlank ? nil : state.bindings.knockMessage) { case .success: state.mode = .knocked diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EncryptedRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EncryptedRoomTimelineView.swift index ff3f5d3697..4febbbdc3a 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EncryptedRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EncryptedRoomTimelineView.swift @@ -17,7 +17,9 @@ struct EncryptedRoomTimelineView: View { switch cause { case .unknown: return \.time - case .membership: + case .sentBeforeWeJoined, + .verificationViolation, + .insecureDevice: return \.block } default: @@ -90,7 +92,7 @@ struct EncryptedRoomTimelineView_Previews: PreviewProvider, TestablePreview { private static func expectedItemWith(timestamp: String, isOutgoing: Bool, senderId: String) -> EncryptedRoomTimelineItem { EncryptedRoomTimelineItem(id: .randomEvent, body: L10n.commonUnableToDecryptNoAccess, - encryptionType: .megolmV1AesSha2(sessionID: "foo", cause: .membership), + encryptionType: .megolmV1AesSha2(sessionID: "foo", cause: .sentBeforeWeJoined), timestamp: timestamp, isOutgoing: isOutgoing, isEditable: false, diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index be954997a9..643b8844ef 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -415,9 +415,9 @@ class ClientProxy: ClientProxyProtocol { } } - func knockRoom(_ roomID: String, message: String?) async -> Result { + func knockRoom(_ roomID: String, via: [String], message: String?) async -> Result { do { - let _ = try await client.knock(roomIdOrAlias: roomID) + let _ = try await client.knock(roomIdOrAlias: roomID, reason: nil, serverNames: via) await waitForRoomToSync(roomID: roomID, timeout: .seconds(30)) return .success(()) } catch { @@ -428,7 +428,7 @@ class ClientProxy: ClientProxyProtocol { func knockRoomAlias(_ roomAlias: String, message: String?) async -> Result { do { - let room = try await client.knock(roomIdOrAlias: roomAlias) + let room = try await client.knock(roomIdOrAlias: roomAlias, reason: nil, serverNames: []) await waitForRoomToSync(roomID: room.id(), timeout: .seconds(30)) return .success(()) } catch { diff --git a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift index d524dbb435..8bbf4ecb55 100644 --- a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift +++ b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift @@ -136,7 +136,7 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol { func joinRoomAlias(_ roomAlias: String) async -> Result - func knockRoom(_ roomID: String, message: String?) async -> Result + func knockRoom(_ roomID: String, via: [String], message: String?) async -> Result func knockRoomAlias(_ roomAlias: String, message: String?) async -> Result diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomEventStringBuilder.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomEventStringBuilder.swift index 55a05efd2a..a5948fc4e7 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomEventStringBuilder.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomEventStringBuilder.swift @@ -26,7 +26,9 @@ struct RoomEventStringBuilder { switch eventItemProxy.content { case .unableToDecrypt(let encryptedMessage): let errorMessage = switch encryptedMessage { - case .megolmV1AesSha2(_, .membership): L10n.commonUnableToDecryptNoAccess + case .megolmV1AesSha2(_, .sentBeforeWeJoined): L10n.commonUnableToDecryptNoAccess + case .megolmV1AesSha2(_, .verificationViolation): L10n.commonUnableToDecryptVerificationViolation + case .megolmV1AesSha2(_, .unknownDevice), .megolmV1AesSha2(_, .unsignedDevice): L10n.commonUnableToDecryptInsecureDevice default: L10n.commonWaitingForDecryptionKey } return prefix(errorMessage, with: displayName) diff --git a/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift b/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift index bb939082f8..346fad5845 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift @@ -83,18 +83,19 @@ class EventTimelineItemProxy { } switch localSendState { - case .sendingFailed(_, let isRecoverable): - return isRecoverable ? .sending : .sendingFailed(.unknown) + case .sendingFailed(let error, let isRecoverable): + switch error { + case .identityViolations(let users): + return .sendingFailed(.verifiedUser(.changedIdentity(users: users))) + case .insecureDevices(let userDeviceMap): + return .sendingFailed(.verifiedUser(.hasUnsignedDevice(devices: userDeviceMap))) + default: + return .sendingFailed(.unknown) + } case .notSentYet: return .sending case .sent: return .sent - case .verifiedUserHasUnsignedDevice(devices: let devices): - return .sendingFailed(.verifiedUser(.hasUnsignedDevice(devices: devices))) - case .verifiedUserChangedIdentity(users: let users): - return .sendingFailed(.verifiedUser(.changedIdentity(users: users))) - case .crossSigningNotSetup, .sendingFromUnverifiedDevice: - return .sendingFailed(.unknown) } }() diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Other/EncryptedRoomTimelineItem.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Other/EncryptedRoomTimelineItem.swift index 8eefe273fe..eaed672901 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Other/EncryptedRoomTimelineItem.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Other/EncryptedRoomTimelineItem.swift @@ -15,7 +15,9 @@ struct EncryptedRoomTimelineItem: EventBasedTimelineItemProtocol, Equatable { } enum UTDCause: Hashable { - case membership + case sentBeforeWeJoined + case verificationViolation + case insecureDevice case unknown } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift index f43d5e4c83..7a3d1e9ba3 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift @@ -157,8 +157,14 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { case .unknown: encryptionType = .megolmV1AesSha2(sessionID: sessionID, cause: .unknown) errorLabel = L10n.commonWaitingForDecryptionKey - case .membership: - encryptionType = .megolmV1AesSha2(sessionID: sessionID, cause: .membership) + case .verificationViolation: + encryptionType = .megolmV1AesSha2(sessionID: sessionID, cause: .verificationViolation) + errorLabel = L10n.commonUnableToDecryptVerificationViolation + case .unsignedDevice, .unknownDevice: + encryptionType = .megolmV1AesSha2(sessionID: sessionID, cause: .insecureDevice) + errorLabel = L10n.commonUnableToDecryptInsecureDevice + case .sentBeforeWeJoined: + encryptionType = .megolmV1AesSha2(sessionID: sessionID, cause: .sentBeforeWeJoined) errorLabel = L10n.commonUnableToDecryptNoAccess } case .olmV1Curve25519AesSha2(let senderKey): diff --git a/project.yml b/project.yml index 250bbe97ca..139fc42119 100644 --- a/project.yml +++ b/project.yml @@ -60,7 +60,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/element-hq/matrix-rust-components-swift - exactVersion: 1.0.60 + exactVersion: 1.0.61 # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios @@ -68,7 +68,7 @@ packages: # path: ../compound-ios AnalyticsEvents: url: https://github.com/matrix-org/matrix-analytics-events - minorVersion: 0.25.0 + minorVersion: 0.27.0 # path: ../matrix-analytics-events Emojibase: url: https://github.com/matrix-org/emojibase-bindings From 926924912ff5af843f8526235f68099460b2573c Mon Sep 17 00:00:00 2001 From: Element CI Date: Thu, 24 Oct 2024 05:53:23 -0700 Subject: [PATCH 078/114] Prepare next release --- CHANGES.md | 16 ++++++++++++++++ ElementX.xcodeproj/project.pbxproj | 4 ++-- project.yml | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index d7642718e8..2987f6851c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,19 @@ +## Changes in 1.9.3 (2024-10-24) + +### What's Changed + +🙌 Improvements +* Update HeroImage to match the BigIcon component from Compound. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3439 +* Update compound to change checkmark color by @Velin92 in https://github.com/element-hq/element-x-ios/pull/3440 + +🐛 Bugfixes +* Fix a bug where the pinned items banner could overlay the composer. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3441 +* Fix composer mention pills showing up as file icons on first use on iOS 18 by @stefanceriu in https://github.com/element-hq/element-x-ios/pull/3444 +* Fix a bug where the room state wouldn't indicate when a call was in progress. by @pixlwave in https://github.com/element-hq/element-x-ios/pull/3442 + + +**Full Changelog**: https://github.com/element-hq/element-x-ios/compare/1.9.2...1.9.3 + ## Changes in 1.9.2 (2024-10-23) ### What's Changed diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index b21e08b5b3..7f8a5ae21c 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7461,7 +7461,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.9.3; + MARKETING_VERSION = 1.9.4; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCTION_APP_NAME = Element; @@ -7538,7 +7538,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.9.3; + MARKETING_VERSION = 1.9.4; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; diff --git a/project.yml b/project.yml index 139fc42119..16f5a1bb84 100644 --- a/project.yml +++ b/project.yml @@ -41,7 +41,7 @@ settings: APP_GROUP_IDENTIFIER: group.$(BASE_APP_GROUP_IDENTIFIER) APP_NAME: ElementX KEYCHAIN_ACCESS_GROUP_IDENTIFIER: "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)" - MARKETING_VERSION: 1.9.3 + MARKETING_VERSION: 1.9.4 CURRENT_PROJECT_VERSION: 1 SUPPORTS_MACCATALYST: false From 7d373c07a3fc081c6cfa3bd0522354f0967ec8b2 Mon Sep 17 00:00:00 2001 From: Mauro <34335419+Velin92@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:21:28 +0200 Subject: [PATCH 079/114] remove iOS 16 support (#3443) * remove iOS 16 support * remove iOS 16 support * test updates * added more delay * revert * adding waiting time on the CI --- ElementX.xcodeproj/project.pbxproj | 6 ++---- ElementX/Sources/Application/AppCoordinator.swift | 2 +- .../Navigation/NavigationCoordinators.swift | 8 ++++---- ElementX/Sources/Other/Pills/PillView.swift | 2 +- ElementX/Sources/Other/SwiftUI/Search.swift | 2 +- .../AppLock/AppLockScreen/View/AppLockScreen.swift | 2 +- .../AppLockSetupPINScreen/View/PINTextField.swift | 2 +- .../View/AppLockSetupSettingsScreen.swift | 2 +- .../Authentication/LoginScreen/View/LoginScreen.swift | 10 ++++++---- .../View/ServerSelectionScreen.swift | 4 ++-- .../Screens/BugReportScreen/View/BugReportScreen.swift | 2 +- .../Screens/CreatePollScreen/View/PollFormScreen.swift | 4 ++-- .../EmojiPickerScreen/View/EmojiPickerScreen.swift | 2 +- .../GlobalSearchScreen/View/GlobalSearchScreen.swift | 2 +- .../Screens/HomeScreen/View/HomeScreenContent.swift | 4 ++-- .../InviteUsersScreen/View/InviteUsersScreen.swift | 2 +- .../Screens/JoinRoomScreen/View/JoinRoomScreen.swift | 2 +- .../View/IdentityConfirmationScreen.swift | 2 +- .../View/RoomChangeRolesScreen.swift | 4 ++-- .../RoomDetailsScreen/View/RoomDetailsScreen.swift | 2 +- .../View/RoomNotificationSettingsScreen.swift | 2 +- .../View/RoomPollsHistoryScreen.swift | 4 ++-- .../ComposerToolbar/View/ComposerToolbar.swift | 8 ++++---- .../View/VoiceMessagePreviewComposer.swift | 4 ++-- .../Screens/RoomScreen/View/SwipeRightAction.swift | 4 ++-- .../View/SecureBackupRecoveryKeyScreen.swift | 2 +- .../View/AnalyticsSettingsScreen.swift | 2 +- .../View/NotificationSettingsScreen.swift | 8 ++++---- .../View/Style/TimelineItemBubbledStylerView.swift | 3 ++- .../Screens/Timeline/View/Style/TimelineStyler.swift | 2 +- .../View/Supplementary/ReactionsSummaryView.swift | 2 +- .../Screens/Timeline/View/TypingIndicatorView.swift | 2 +- .../VoiceMessages/VoiceMessageRoomPlaybackView.swift | 4 ++-- ...bledStylerView-iPad-en-GB.Encryption-Indicators.png | 4 ++-- ...mBubbledStylerView-iPad-en-GB.Mock-Timeline-RTL.png | 4 ++-- ...eItemBubbledStylerView-iPad-en-GB.Mock-Timeline.png | 4 ++-- ...imelineItemBubbledStylerView-iPad-en-GB.Replies.png | 4 ++-- ...ledStylerView-iPad-pseudo.Encryption-Indicators.png | 4 ++-- ...BubbledStylerView-iPad-pseudo.Mock-Timeline-RTL.png | 4 ++-- ...ItemBubbledStylerView-iPad-pseudo.Mock-Timeline.png | 4 ++-- ...melineItemBubbledStylerView-iPad-pseudo.Replies.png | 4 ++-- ...tylerView-iPhone-16-en-GB.Encryption-Indicators.png | 4 ++-- ...ledStylerView-iPhone-16-en-GB.Mock-Timeline-RTL.png | 4 ++-- ...BubbledStylerView-iPhone-16-en-GB.Mock-Timeline.png | 4 ++-- ...neItemBubbledStylerView-iPhone-16-en-GB.Replies.png | 4 ++-- ...ylerView-iPhone-16-pseudo.Encryption-Indicators.png | 4 ++-- ...edStylerView-iPhone-16-pseudo.Mock-Timeline-RTL.png | 4 ++-- ...ubbledStylerView-iPhone-16-pseudo.Mock-Timeline.png | 4 ++-- ...eItemBubbledStylerView-iPhone-16-pseudo.Replies.png | 4 ++-- .../ElementX/View/TemplateScreen.swift | 2 +- project.yml | 3 +-- 51 files changed, 91 insertions(+), 91 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 7f8a5ae21c..07e8e2e7e0 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7458,9 +7458,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.4; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; - MACOSX_DEPLOYMENT_TARGET = 13.3; MARKETING_VERSION = 1.9.4; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -7535,9 +7534,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.4; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; - MACOSX_DEPLOYMENT_TARGET = 13.3; MARKETING_VERSION = 1.9.4; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index 75fa3da1d3..1b878c7851 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -575,7 +575,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg // The user will log out, clear any existing notifications and unregister from receving new ones UNUserNotificationCenter.current().removeAllPendingNotificationRequests() UNUserNotificationCenter.current().removeAllDeliveredNotifications() - UIApplication.shared.applicationIconBadgeNumber = 0 + UNUserNotificationCenter.current().setBadgeCount(0) unregisterForRemoteNotifications() diff --git a/ElementX/Sources/Application/Navigation/NavigationCoordinators.swift b/ElementX/Sources/Application/Navigation/NavigationCoordinators.swift index 7441c85dfb..e17c247812 100644 --- a/ElementX/Sources/Application/Navigation/NavigationCoordinators.swift +++ b/ElementX/Sources/Application/Navigation/NavigationCoordinators.swift @@ -452,15 +452,15 @@ private struct NavigationSplitCoordinatorView: View { } // Handle `horizontalSizeClass` changes breaking the navigation bar // https://github.com/element-hq/element-x-ios/issues/617 - .onChange(of: horizontalSizeClass) { value in + .onChange(of: horizontalSizeClass) { _, newValue in guard scenePhase != .background else { return } - isInSplitMode = value == .regular + isInSplitMode = newValue == .regular } - .onChange(of: scenePhase) { value in - guard value == .active else { + .onChange(of: scenePhase) { _, newValue in + guard newValue == .active else { return } diff --git a/ElementX/Sources/Other/Pills/PillView.swift b/ElementX/Sources/Other/Pills/PillView.swift index 2a0deee763..2e51831a2d 100644 --- a/ElementX/Sources/Other/Pills/PillView.swift +++ b/ElementX/Sources/Other/Pills/PillView.swift @@ -30,7 +30,7 @@ struct PillView: View { .padding(.trailing, 6) .padding(.vertical, 1) .background { Capsule().foregroundColor(backgroundColor) } - .onChange(of: context.viewState.displayText) { _ in + .onChange(of: context.viewState.displayText) { didChangeText() } } diff --git a/ElementX/Sources/Other/SwiftUI/Search.swift b/ElementX/Sources/Other/SwiftUI/Search.swift index e9ba319837..cb951305d9 100644 --- a/ElementX/Sources/Other/SwiftUI/Search.swift +++ b/ElementX/Sources/Other/SwiftUI/Search.swift @@ -171,7 +171,7 @@ struct IsSearchingModifier: ViewModifier { func body(content: Content) -> some View { content - .onChange(of: isSearchingEnv) { isSearching = $0 } + .onChange(of: isSearchingEnv) { isSearching = $1 } } } diff --git a/ElementX/Sources/Screens/AppLock/AppLockScreen/View/AppLockScreen.swift b/ElementX/Sources/Screens/AppLock/AppLockScreen/View/AppLockScreen.swift index f607fbdc38..45c20b95b4 100644 --- a/ElementX/Sources/Screens/AppLock/AppLockScreen/View/AppLockScreen.swift +++ b/ElementX/Sources/Screens/AppLock/AppLockScreen/View/AppLockScreen.swift @@ -29,7 +29,7 @@ struct AppLockScreen: View { pinInputField .padding(.bottom, 16) .offset(x: pinInputFieldOffset) - .onChange(of: context.viewState.numberOfPINAttempts) { newValue in + .onChange(of: context.viewState.numberOfPINAttempts) { _, newValue in guard newValue > 0 else { return } // Reset without animation in Previews. accessibilitySubtitleFocus = true Task { await animatePINFailure() } diff --git a/ElementX/Sources/Screens/AppLock/AppLockSetupPINScreen/View/PINTextField.swift b/ElementX/Sources/Screens/AppLock/AppLockSetupPINScreen/View/PINTextField.swift index 09f10d3655..408a7f8381 100644 --- a/ElementX/Sources/Screens/AppLock/AppLockSetupPINScreen/View/PINTextField.swift +++ b/ElementX/Sources/Screens/AppLock/AppLockSetupPINScreen/View/PINTextField.swift @@ -20,7 +20,7 @@ struct PINTextField: View { .textFieldStyle(PINTextFieldStyle(pinCode: pinCode, isSecure: isSecure, maxLength: maxLength, size: size)) .keyboardType(.numberPad) .accessibilityIdentifier(A11yIdentifiers.appLockSetupPINScreen.textField) - .onChange(of: pinCode) { newValue in + .onChange(of: pinCode) { _, newValue in let sanitized = sanitize(newValue) if sanitized != newValue { MXLog.warning("PIN code input sanitized.") diff --git a/ElementX/Sources/Screens/AppLock/AppLockSetupSettingsScreen/View/AppLockSetupSettingsScreen.swift b/ElementX/Sources/Screens/AppLock/AppLockSetupSettingsScreen/View/AppLockSetupSettingsScreen.swift index 097b27079c..3cb1dfc362 100644 --- a/ElementX/Sources/Screens/AppLock/AppLockSetupSettingsScreen/View/AppLockSetupSettingsScreen.swift +++ b/ElementX/Sources/Screens/AppLock/AppLockSetupSettingsScreen/View/AppLockSetupSettingsScreen.swift @@ -29,7 +29,7 @@ struct AppLockSetupSettingsScreen: View { Section { ListRow(label: .plain(title: context.viewState.enableBiometricsTitle), kind: .toggle($context.enableBiometrics)) - .onChange(of: context.enableBiometrics) { _ in + .onChange(of: context.enableBiometrics) { context.send(viewAction: .enableBiometricsChanged) } } diff --git a/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginScreen.swift b/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginScreen.swift index b5e882ee63..3226fcd928 100644 --- a/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginScreen.swift +++ b/ElementX/Sources/Screens/Authentication/LoginScreen/View/LoginScreen.swift @@ -74,7 +74,9 @@ struct LoginScreen: View { .textContentType(.username) .autocapitalization(.none) .submitLabel(.next) - .onChange(of: isUsernameFocused, perform: usernameFocusChanged) + .onChange(of: isUsernameFocused) { _, newValue in + usernameFocusChanged(isFocussed: newValue) + } .onSubmit { isPasswordFocused = true } .padding(.bottom, 20) @@ -135,19 +137,19 @@ struct LoginScreen_Previews: PreviewProvider, TestablePreview { LoginScreen(context: viewModel.context) } .previewDisplayName("matrix.org") - .snapshotPreferences(delay: 0.1) + .snapshotPreferences(delay: 1) NavigationStack { LoginScreen(context: credentialsViewModel.context) } .previewDisplayName("Credentials Entered") - .snapshotPreferences(delay: 0.1) + .snapshotPreferences(delay: 1) NavigationStack { LoginScreen(context: unconfiguredViewModel.context) } .previewDisplayName("Unsupported") - .snapshotPreferences(delay: 0.1) + .snapshotPreferences(delay: 1) } static func makeViewModel(homeserverAddress: String = "matrix.org", withCredentials: Bool = false) -> LoginScreenViewModel { diff --git a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/View/ServerSelectionScreen.swift b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/View/ServerSelectionScreen.swift index 49e792fcb8..b39a92e416 100644 --- a/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/View/ServerSelectionScreen.swift +++ b/ElementX/Sources/Screens/Authentication/ServerSelectionScreen/View/ServerSelectionScreen.swift @@ -59,7 +59,7 @@ struct ServerSelectionScreen: View { .keyboardType(.URL) .autocapitalization(.none) .disableAutocorrection(true) - .onChange(of: context.homeserverAddress) { _ in context.send(viewAction: .clearFooterError) } + .onChange(of: context.homeserverAddress) { context.send(viewAction: .clearFooterError) } .submitLabel(.done) .onSubmit(submit) @@ -107,7 +107,7 @@ struct ServerSelection_Previews: PreviewProvider, TestablePreview { NavigationStack { ServerSelectionScreen(context: invalidViewModel.context) } - .snapshotPreferences(delay: 0.25) + .snapshotPreferences(delay: 1) } static func makeViewModel(for homeserverAddress: String) -> ServerSelectionScreenViewModel { diff --git a/ElementX/Sources/Screens/BugReportScreen/View/BugReportScreen.swift b/ElementX/Sources/Screens/BugReportScreen/View/BugReportScreen.swift index d972d1279f..1be9527292 100644 --- a/ElementX/Sources/Screens/BugReportScreen/View/BugReportScreen.swift +++ b/ElementX/Sources/Screens/BugReportScreen/View/BugReportScreen.swift @@ -30,7 +30,7 @@ struct BugReportScreen: View { .navigationBarTitleDisplayMode(.inline) .toolbar { toolbar } .interactiveDismissDisabled() - .onChange(of: selectedScreenshot) { newItem in + .onChange(of: selectedScreenshot) { _, newItem in Task { guard let data = try? await newItem?.loadTransferable(type: Data.self), let image = UIImage(data: data) diff --git a/ElementX/Sources/Screens/CreatePollScreen/View/PollFormScreen.swift b/ElementX/Sources/Screens/CreatePollScreen/View/PollFormScreen.swift index 16f85e5299..8ba9f9c7da 100644 --- a/ElementX/Sources/Screens/CreatePollScreen/View/PollFormScreen.swift +++ b/ElementX/Sources/Screens/CreatePollScreen/View/PollFormScreen.swift @@ -65,8 +65,8 @@ struct PollFormScreen: View { } .focused($focus, equals: .option(index: index)) .accessibilityIdentifier(A11yIdentifiers.pollFormScreen.optionID(index)) - .onChange(of: context.options[index].text) { optionText in - guard let lastCharacter = optionText.last, lastCharacter.isNewline else { + .onChange(of: context.options[index].text) { _, newOptionText in + guard let lastCharacter = newOptionText.last, lastCharacter.isNewline else { return } diff --git a/ElementX/Sources/Screens/EmojiPickerScreen/View/EmojiPickerScreen.swift b/ElementX/Sources/Screens/EmojiPickerScreen/View/EmojiPickerScreen.swift index ddbbeb75be..9f5e39325f 100644 --- a/ElementX/Sources/Screens/EmojiPickerScreen/View/EmojiPickerScreen.swift +++ b/ElementX/Sources/Screens/EmojiPickerScreen/View/EmojiPickerScreen.swift @@ -55,7 +55,7 @@ struct EmojiPickerScreen: View { } .presentationDetents([.medium, .large]) .presentationDragIndicator(isSearching ? .hidden : .visible) - .onChange(of: searchString) { _ in + .onChange(of: searchString) { context.send(viewAction: .search(searchString: searchString)) } } diff --git a/ElementX/Sources/Screens/GlobalSearchScreen/View/GlobalSearchScreen.swift b/ElementX/Sources/Screens/GlobalSearchScreen/View/GlobalSearchScreen.swift index 388ee539ae..f7f4a6fd00 100644 --- a/ElementX/Sources/Screens/GlobalSearchScreen/View/GlobalSearchScreen.swift +++ b/ElementX/Sources/Screens/GlobalSearchScreen/View/GlobalSearchScreen.swift @@ -49,7 +49,7 @@ struct GlobalSearchScreen: View { selectedRoom = context.viewState.rooms.first searchFieldFocus = true } - .onChange(of: context.viewState.rooms) { _ in + .onChange(of: context.viewState.rooms) { selectedRoom = context.viewState.rooms.first } .onTapGesture { diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenContent.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenContent.swift index ec65e9420a..7275684ddd 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenContent.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenContent.swift @@ -66,10 +66,10 @@ struct HomeScreenContent: View { .onReceive(scrollViewAdapter.isScrolling) { _ in updateVisibleRange() } - .onChange(of: context.searchQuery) { _ in + .onChange(of: context.searchQuery) { updateVisibleRange() } - .onChange(of: context.viewState.visibleRooms) { _ in + .onChange(of: context.viewState.visibleRooms) { updateVisibleRange() // We have been seeing a lot of issues around the room list not updating properly after diff --git a/ElementX/Sources/Screens/InviteUsersScreen/View/InviteUsersScreen.swift b/ElementX/Sources/Screens/InviteUsersScreen/View/InviteUsersScreen.swift index d677031815..5e75198d69 100644 --- a/ElementX/Sources/Screens/InviteUsersScreen/View/InviteUsersScreen.swift +++ b/ElementX/Sources/Screens/InviteUsersScreen/View/InviteUsersScreen.swift @@ -110,7 +110,7 @@ struct InviteUsersScreen: View { .frame(width: cellWidth) } } - .onChange(of: context.viewState.scrollToLastID) { lastAddedID in + .onChange(of: context.viewState.scrollToLastID) { _, lastAddedID in guard let id = lastAddedID else { return } withElementAnimation(.easeInOut) { scrollView.scrollTo(id) diff --git a/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift b/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift index 87035a6a78..419e463297 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift @@ -107,7 +107,7 @@ struct JoinRoomScreen: View { VStack(alignment: .leading, spacing: 12) { HStack(spacing: 0) { TextField("", text: $context.knockMessage, axis: .vertical) - .onChange(of: context.knockMessage) { newValue in + .onChange(of: context.knockMessage) { _, newValue in context.knockMessage = String(newValue.prefix(1000)) } .lineLimit(4, reservesSpace: true) diff --git a/ElementX/Sources/Screens/Onboarding/IdentityConfirmationScreen/View/IdentityConfirmationScreen.swift b/ElementX/Sources/Screens/Onboarding/IdentityConfirmationScreen/View/IdentityConfirmationScreen.swift index 4bbb249ef3..d81d14944c 100644 --- a/ElementX/Sources/Screens/Onboarding/IdentityConfirmationScreen/View/IdentityConfirmationScreen.swift +++ b/ElementX/Sources/Screens/Onboarding/IdentityConfirmationScreen/View/IdentityConfirmationScreen.swift @@ -108,7 +108,7 @@ struct IdentityConfirmationScreen_Previews: PreviewProvider, TestablePreview { NavigationStack { IdentityConfirmationScreen(context: viewModel.context) } - .snapshotPreferences(delay: 0.25) + .snapshotPreferences(delay: 1) } private static var viewModel: IdentityConfirmationScreenViewModel { diff --git a/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreen.swift b/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreen.swift index 02975c9bcc..d116dc4c58 100644 --- a/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreen.swift +++ b/ElementX/Sources/Screens/RoomChangeRolesScreen/View/RoomChangeRolesScreen.swift @@ -71,8 +71,8 @@ struct RoomChangeRolesScreen: View { .frame(width: cellWidth) } } - .onChange(of: context.viewState.lastPromotedMember) { member in - guard let member else { return } + .onChange(of: context.viewState.lastPromotedMember) { _, newValue in + guard let member = newValue else { return } withElementAnimation(.easeInOut) { scrollView.scrollTo(member.id) } diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift b/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift index 1126194ab4..207f10796c 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift @@ -185,7 +185,7 @@ struct RoomDetailsScreen: View { ListRow(label: .default(title: L10n.commonFavourite, icon: \.favourite), kind: .toggle($context.isFavourite)) .accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.favourite) - .onChange(of: context.isFavourite) { newValue in + .onChange(of: context.isFavourite) { _, newValue in context.send(viewAction: .toggleFavourite(isFavourite: newValue)) } diff --git a/ElementX/Sources/Screens/RoomNotificationSettingsScreen/View/RoomNotificationSettingsScreen.swift b/ElementX/Sources/Screens/RoomNotificationSettingsScreen/View/RoomNotificationSettingsScreen.swift index 7c1ae23f76..ef3b58f7d9 100644 --- a/ElementX/Sources/Screens/RoomNotificationSettingsScreen/View/RoomNotificationSettingsScreen.swift +++ b/ElementX/Sources/Screens/RoomNotificationSettingsScreen/View/RoomNotificationSettingsScreen.swift @@ -35,7 +35,7 @@ struct RoomNotificationSettingsScreen: View { kind: .toggle($context.allowCustomSetting)) .accessibilityIdentifier(A11yIdentifiers.roomNotificationSettingsScreen.allowCustomSetting) .disabled(context.viewState.notificationSettingsState.isLoading) - .onChange(of: context.allowCustomSetting) { _ in + .onChange(of: context.allowCustomSetting) { context.send(viewAction: .changedAllowCustomSettings) } } footer: { diff --git a/ElementX/Sources/Screens/RoomPollsHistoryScreen/View/RoomPollsHistoryScreen.swift b/ElementX/Sources/Screens/RoomPollsHistoryScreen/View/RoomPollsHistoryScreen.swift index 96d4c8c548..0029e48b64 100644 --- a/ElementX/Sources/Screens/RoomPollsHistoryScreen/View/RoomPollsHistoryScreen.swift +++ b/ElementX/Sources/Screens/RoomPollsHistoryScreen/View/RoomPollsHistoryScreen.swift @@ -47,8 +47,8 @@ struct RoomPollsHistoryScreen: View { } .pickerStyle(.segmented) .readableFrame(maxWidth: 475) - .onChange(of: context.filter) { value in - context.send(viewAction: .filter(value)) + .onChange(of: context.filter) { _, newValue in + context.send(viewAction: .filter(newValue)) } } diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift index 8f1dcaa6c6..0cb580d0ab 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift @@ -180,18 +180,18 @@ struct ComposerToolbar: View { guard !composerFocused else { return } composerFocused = true } - .onChange(of: context.composerFocused) { newValue in + .onChange(of: context.composerFocused) { _, newValue in guard composerFocused != newValue else { return } composerFocused = newValue } - .onChange(of: composerFocused) { newValue in + .onChange(of: composerFocused) { _, newValue in context.composerFocused = newValue } - .onChange(of: context.plainComposerText) { _ in + .onChange(of: context.plainComposerText) { context.send(viewAction: .plainComposerTextChanged) } - .onChange(of: context.composerFormattingEnabled) { _ in + .onChange(of: context.composerFormattingEnabled) { context.send(viewAction: .didToggleFormattingOptions) } .onAppear { diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/VoiceMessagePreviewComposer.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/VoiceMessagePreviewComposer.swift index f290780761..0e20440141 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/VoiceMessagePreviewComposer.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/VoiceMessagePreviewComposer.swift @@ -48,8 +48,8 @@ struct VoiceMessagePreviewComposer: View { showCursor: playerState.showProgressIndicator, onSeek: onSeek) } - .onChange(of: isDragging) { isDragging in - onScrubbing(isDragging) + .onChange(of: isDragging) { _, newValue in + onScrubbing(newValue) } .padding(.vertical, 4.0) .padding(.horizontal, 6.0) diff --git a/ElementX/Sources/Screens/RoomScreen/View/SwipeRightAction.swift b/ElementX/Sources/Screens/RoomScreen/View/SwipeRightAction.swift index 183af7a4ca..df0a3f5df9 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/SwipeRightAction.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/SwipeRightAction.swift @@ -32,8 +32,8 @@ struct SwipeRightAction: ViewModifier { .offset(x: xOffset, y: 0.0) .animation(.interactiveSpring().speed(0.5), value: xOffset) .timelineGesture(gesture) - .onChange(of: dragGestureActive) { value in - if value == true { + .onChange(of: dragGestureActive) { _, newValue in + if newValue == true { if shouldStartAction() { feedbackGenerator.prepare() canStartAction = true diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift index 00f980ab5a..995963a445 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift @@ -19,7 +19,7 @@ struct SecureBackupRecoveryKeyScreen: View { ScrollViewReader { reader in mainContent .padding(16) - .onChange(of: focused) { newValue in + .onChange(of: focused) { _, newValue in guard newValue == true else { return } reader.scrollTo(textFieldIdentifier) } diff --git a/ElementX/Sources/Screens/Settings/AnalyticsSettingsScreen/View/AnalyticsSettingsScreen.swift b/ElementX/Sources/Screens/Settings/AnalyticsSettingsScreen/View/AnalyticsSettingsScreen.swift index 1972bdabba..916a9e6c23 100644 --- a/ElementX/Sources/Screens/Settings/AnalyticsSettingsScreen/View/AnalyticsSettingsScreen.swift +++ b/ElementX/Sources/Screens/Settings/AnalyticsSettingsScreen/View/AnalyticsSettingsScreen.swift @@ -24,7 +24,7 @@ struct AnalyticsSettingsScreen: View { Section { ListRow(label: .plain(title: L10n.screenAnalyticsSettingsShareData), kind: .toggle($context.enableAnalytics)) - .onChange(of: context.enableAnalytics) { _ in + .onChange(of: context.enableAnalytics) { context.send(viewAction: .toggleAnalytics) } } footer: { diff --git a/ElementX/Sources/Screens/Settings/NotificationSettingsScreen/View/NotificationSettingsScreen.swift b/ElementX/Sources/Screens/Settings/NotificationSettingsScreen/View/NotificationSettingsScreen.swift index dca3369aac..2c0319c140 100644 --- a/ElementX/Sources/Screens/Settings/NotificationSettingsScreen/View/NotificationSettingsScreen.swift +++ b/ElementX/Sources/Screens/Settings/NotificationSettingsScreen/View/NotificationSettingsScreen.swift @@ -86,7 +86,7 @@ struct NotificationSettingsScreen: View { Section { ListRow(label: .plain(title: L10n.screenNotificationSettingsEnableNotifications), kind: .toggle($context.enableNotifications)) - .onChange(of: context.enableNotifications) { _ in + .onChange(of: context.enableNotifications) { context.send(viewAction: .changedEnableNotifications) } } @@ -128,7 +128,7 @@ struct NotificationSettingsScreen: View { kind: .toggle($context.roomMentionsEnabled)) .disabled(context.viewState.settings?.roomMentionsEnabled == nil) .allowsHitTesting(!context.viewState.applyingChange) - .onChange(of: context.roomMentionsEnabled) { _ in + .onChange(of: context.roomMentionsEnabled) { context.send(viewAction: .roomMentionChanged) } } header: { @@ -143,7 +143,7 @@ struct NotificationSettingsScreen: View { kind: .toggle($context.callsEnabled)) .disabled(context.viewState.settings?.callsEnabled == nil) .allowsHitTesting(!context.viewState.applyingChange) - .onChange(of: context.callsEnabled) { _ in + .onChange(of: context.callsEnabled) { context.send(viewAction: .callsChanged) } } header: { @@ -158,7 +158,7 @@ struct NotificationSettingsScreen: View { kind: .toggle($context.invitationsEnabled)) .disabled(context.viewState.settings?.invitationsEnabled == nil) .allowsHitTesting(!context.viewState.applyingChange) - .onChange(of: context.invitationsEnabled) { _ in + .onChange(of: context.invitationsEnabled) { context.send(viewAction: .invitationsChanged) } } header: { diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift index 0d3fc5360e..049a788311 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift @@ -377,11 +377,12 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview .previewDisplayName("Replies") threads .previewDisplayName("Thread decorator") + .snapshotPreferences(delay: 1) encryptionAuthenticity .previewDisplayName("Encryption Indicators") pinned .previewDisplayName("Pinned messages") - .snapshotPreferences(delay: 2.0) + .snapshotPreferences(delay: 1) } static var mockTimeline: some View { diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift index 1cafaf023e..f69da64db7 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift @@ -25,7 +25,7 @@ struct TimelineStyler: View { var body: some View { mainContent - .onChange(of: timelineItem.properties.deliveryStatus) { newStatus in + .onChange(of: timelineItem.properties.deliveryStatus) { _, newStatus in if case .sendingFailed = newStatus { guard task == nil else { return diff --git a/ElementX/Sources/Screens/Timeline/View/Supplementary/ReactionsSummaryView.swift b/ElementX/Sources/Screens/Timeline/View/Supplementary/ReactionsSummaryView.swift index 1920f2d64f..fccaf576c3 100644 --- a/ElementX/Sources/Screens/Timeline/View/Supplementary/ReactionsSummaryView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Supplementary/ReactionsSummaryView.swift @@ -43,7 +43,7 @@ struct ReactionsSummaryView: View { scrollView.scrollTo(selectedReactionKey) } } - .onChange(of: selectedReactionKey) { _ in + .onChange(of: selectedReactionKey) { scrollView.scrollTo(selectedReactionKey) } } diff --git a/ElementX/Sources/Screens/Timeline/View/TypingIndicatorView.swift b/ElementX/Sources/Screens/Timeline/View/TypingIndicatorView.swift index da3d805e2c..e927cda08d 100644 --- a/ElementX/Sources/Screens/Timeline/View/TypingIndicatorView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TypingIndicatorView.swift @@ -19,7 +19,7 @@ struct TypingIndicatorView: View { .truncationMode(.middle) .padding(.horizontal, 4) .animation(.elementDefault, value: typingMembers.members) - .onChange(of: typingMembers.members) { newValue in + .onChange(of: typingMembers.members) { _, newValue in if !newValue.isEmpty { didShowTextOnce = true } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift index 1cda7b3bda..1928a55b4a 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift @@ -51,8 +51,8 @@ struct VoiceMessageRoomPlaybackView: View { } .padding(.leading, 2) .padding(.trailing, 8) - .onChange(of: isDragging) { isDragging in - onScrubbing(isDragging) + .onChange(of: isDragging) { _, newValue in + onScrubbing(newValue) } } diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-en-GB.Encryption-Indicators.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-en-GB.Encryption-Indicators.png index ec2c0f5963..c6b62565fc 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-en-GB.Encryption-Indicators.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-en-GB.Encryption-Indicators.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:517d2adbb60ff9e40d7b0f530b05f3810f302180fed9259673afe57f2598d669 -size 174777 +oid sha256:27cf16e147a82ad2126e43a511d07ced65468b0c9ec18f86e30a1546ef9a0864 +size 174751 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-en-GB.Mock-Timeline-RTL.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-en-GB.Mock-Timeline-RTL.png index f5e3ce2a35..54f4895512 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-en-GB.Mock-Timeline-RTL.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-en-GB.Mock-Timeline-RTL.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9a505f51ed4ce883a44c63856329f468b285480cf38d1f7e9f9dddd39beacd1 -size 286018 +oid sha256:17403a44b5f71ef51b12a2392d3c8eed2b30cd4ddeb25188c29e738f8ed3c3fc +size 285676 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-en-GB.Mock-Timeline.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-en-GB.Mock-Timeline.png index 89ba4a3340..1e57c3f8e0 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-en-GB.Mock-Timeline.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-en-GB.Mock-Timeline.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ededd3cbc04f946fd5374d1f43ec0d9fe6a1537aae193a7dffbf9dc5ce2c2ff1 -size 284462 +oid sha256:2f576e2c44ecda928a8afdbd29bf9c79631517d9c42d6b8a16b1dcdbac1885a9 +size 284180 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-en-GB.Replies.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-en-GB.Replies.png index 0e89eb6288..037b9bfd35 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-en-GB.Replies.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-en-GB.Replies.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c7524b103a29f907484c183458dca038ccfe5d4396328fd774ce15d321eea13 -size 107183 +oid sha256:c4563bc0de73f8053c8c6a8ab08aea50ee6a5328aa46a5c627f37dfb1bdd6f5b +size 107113 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-pseudo.Encryption-Indicators.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-pseudo.Encryption-Indicators.png index 7d4aeba08c..21ddc92ad4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-pseudo.Encryption-Indicators.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-pseudo.Encryption-Indicators.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:685784026c5fbf1676f2e41105303c150a8520c0588081960dd6c679cb16f86f -size 176916 +oid sha256:7a1ff79835b6de47a22fdbdca05a56a257aa5341da6d0515e02324570c70e05d +size 176994 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-pseudo.Mock-Timeline-RTL.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-pseudo.Mock-Timeline-RTL.png index 0239b99399..f2545df7ae 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-pseudo.Mock-Timeline-RTL.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-pseudo.Mock-Timeline-RTL.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aaea7ab92cc40488cccadca3b281008eb829e49f99b9f55c4dad405afbf5d6ae -size 287689 +oid sha256:fc726f43094e192a9f367c0ece419d581de5d43bab53275eaa73ae25969e65ab +size 287326 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-pseudo.Mock-Timeline.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-pseudo.Mock-Timeline.png index 6452a271ee..e9f3a9cb70 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-pseudo.Mock-Timeline.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-pseudo.Mock-Timeline.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:842fd304e64840dc39ad6231c8a52981de84d23d212229051c4b7cc2649fde28 -size 285736 +oid sha256:982eb9dd6ed5fe3c4c13811ae463b80f1b571fec18953d98d641d367cd705db0 +size 285493 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-pseudo.Replies.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-pseudo.Replies.png index 0e89eb6288..037b9bfd35 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-pseudo.Replies.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPad-pseudo.Replies.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c7524b103a29f907484c183458dca038ccfe5d4396328fd774ce15d321eea13 -size 107183 +oid sha256:c4563bc0de73f8053c8c6a8ab08aea50ee6a5328aa46a5c627f37dfb1bdd6f5b +size 107113 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Encryption-Indicators.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Encryption-Indicators.png index f2a14f4bac..bc8617ecd5 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Encryption-Indicators.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Encryption-Indicators.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ad54f532f3ec4333e004b763956ea2ffec9aecfa0c3113b423f6a9f85ac87c7 -size 127545 +oid sha256:1af078d4671249c492161013d753905feed81303ce9e172e17e3432c3acfc372 +size 127533 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Mock-Timeline-RTL.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Mock-Timeline-RTL.png index f38c214c08..33f9d4b5d6 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Mock-Timeline-RTL.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Mock-Timeline-RTL.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:799c4d6ea7c9ae76270f0a4e5bdce5f1c8330b59fd0375cfccaa547fe10d9682 -size 190665 +oid sha256:1a39ebd070935b96d697dc0c70244962a8d55c6c9ce3e958808c35f8ed009506 +size 190461 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Mock-Timeline.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Mock-Timeline.png index 5fa5af364d..bbd1e7fb93 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Mock-Timeline.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Mock-Timeline.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:900a641ca6e9d716f2ac6ea0d5fcb0bba0587315250fac01bc63e92242071d33 -size 188360 +oid sha256:e4278c55045120935c02fe570409295a0eb0b984a103e5fd09d314bb2f4e09a6 +size 188620 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Replies.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Replies.png index f65e1a7bae..f54321b33e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Replies.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-en-GB.Replies.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9bf2e4421574f1fdfe25bebb6eafd22bae7e67dd032a4b3bd785dd48cd28b477 -size 65845 +oid sha256:516a4ad7cb46f4ef1ab316e6bea424abe8c5ef4767a2e4d838bccc80bb8db9be +size 65830 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Encryption-Indicators.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Encryption-Indicators.png index 45fc5a690a..8389f9445a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Encryption-Indicators.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Encryption-Indicators.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8dd04554290d3299b0ce0a95d2304bce2d90a7c130f48a64efc6fc4b3b15727c -size 128846 +oid sha256:4bc439fffcd4b212a103493a07f7f0f336c69bd641b86bb84768cc0595a82bd2 +size 128840 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Mock-Timeline-RTL.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Mock-Timeline-RTL.png index 5474306bf1..651a6d750a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Mock-Timeline-RTL.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Mock-Timeline-RTL.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8c11bc7b586274511cbaf4c3a54f5a3fe2ee51bf345d1feadcad327a207df02 -size 188412 +oid sha256:1eb05117714fb7853917a32dba87565ed0ad83b3097ca01703581dc0ec8e7b7a +size 188275 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Mock-Timeline.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Mock-Timeline.png index 6fc5eeb2d9..d9a42bff3f 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Mock-Timeline.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Mock-Timeline.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c4444924cad6f07ea6b85d2ad3f7a2c1f60d6e4d03fc2ea0880df496b157132 -size 186021 +oid sha256:a421129a41149ca0c3be3981ef169ddcbce8b0c57a0b94ea43a3fea2dc4202f8 +size 186287 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Replies.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Replies.png index f65e1a7bae..f54321b33e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Replies.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineItemBubbledStylerView-iPhone-16-pseudo.Replies.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9bf2e4421574f1fdfe25bebb6eafd22bae7e67dd032a4b3bd785dd48cd28b477 -size 65845 +oid sha256:516a4ad7cb46f4ef1ab316e6bea424abe8c5ef4767a2e4d838bccc80bb8db9be +size 65830 diff --git a/Tools/Scripts/Templates/SimpleScreenExample/ElementX/View/TemplateScreen.swift b/Tools/Scripts/Templates/SimpleScreenExample/ElementX/View/TemplateScreen.swift index e5f72310a5..ce5e2d0bec 100644 --- a/Tools/Scripts/Templates/SimpleScreenExample/ElementX/View/TemplateScreen.swift +++ b/Tools/Scripts/Templates/SimpleScreenExample/ElementX/View/TemplateScreen.swift @@ -24,7 +24,7 @@ struct TemplateScreen: View { } .compoundList() .navigationTitle(context.viewState.title) - .onChange(of: context.composerText) { _ in + .onChange(of: context.composerText) { context.send(viewAction: .textChanged) } } diff --git a/project.yml b/project.yml index 16f5a1bb84..51f71ea1c2 100644 --- a/project.yml +++ b/project.yml @@ -10,8 +10,7 @@ options: groupSortPosition: bottom createIntermediateGroups: true deploymentTarget: - iOS: '16.4' - macOS: '13.3' + iOS: '17.6' groupOrdering: - order: - ElementX From 2511c98090cc22865d5ff737c7bbd7884f70bf4d Mon Sep 17 00:00:00 2001 From: Mauro <34335419+Velin92@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:23:06 +0200 Subject: [PATCH 080/114] Knocked Preview implementation (#3426) * JoinRoomScreen ui for knocking * code improvement * updated previews * added knocked state with tests * send knock request * Apply suggestions from code review Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com> * pr comments * update tests * new API * knock implementation and cancel knock * update strings * added a knocked cell in the home screen * design update * updated SDK * simplified the invite case code * pr comments * updated previews * added message as reason * updated strings * fixing tests --------- Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com> --- ElementX.xcodeproj/project.pbxproj | 16 +- .../en.lproj/Localizable.strings | 7 +- ElementX/Sources/Generated/Strings.swift | 12 +- .../Sources/Mocks/InvitedRoomProxyMock.swift | 31 +++ .../Mocks/RoomSummaryProviderMock.swift | 27 +-- .../SwiftUI/Views}/RoomHeaderView.swift | 0 .../Screens/HomeScreen/HomeScreenModels.swift | 30 ++- .../View/HomeScreenInviteCell.swift | 9 +- .../View/HomeScreenKnockedCell.swift | 200 ++++++++++++++++++ .../HomeScreen/View/HomeScreenRoomList.swift | 2 + .../JoinRoomScreenCoordinator.swift | 2 +- .../JoinRoomScreen/JoinRoomScreenModels.swift | 3 +- .../JoinRoomScreenViewModel.swift | 61 ++++-- .../JoinRoomScreen/View/JoinRoomScreen.swift | 34 ++- .../Sources/Services/Client/ClientProxy.swift | 4 +- .../Services/Room/KnockedRoomProxy.swift | 8 +- .../Room/RoomSummary/RoomSummary.swift | 27 ++- .../RoomSummary/RoomSummaryProvider.swift | 9 +- .../Timeline/TimelineProxyProtocol.swift | 1 - .../Sources/GeneratedPreviewTests.swift | 6 + ...est_homeScreenKnockedCell-iPad-en-GB.1.png | 3 + ...st_homeScreenKnockedCell-iPad-pseudo.1.png | 3 + ...omeScreenKnockedCell-iPhone-16-en-GB.1.png | 3 + ...meScreenKnockedCell-iPhone-16-pseudo.1.png | 3 + .../test_joinRoomScreen-iPad-en-GB.Invite.png | 4 +- .../test_joinRoomScreen-iPad-en-GB.Join.png | 4 +- .../test_joinRoomScreen-iPad-en-GB.Knock.png | 4 +- ...test_joinRoomScreen-iPad-en-GB.Knocked.png | 4 +- ...test_joinRoomScreen-iPad-en-GB.Unknown.png | 4 +- ...test_joinRoomScreen-iPad-pseudo.Invite.png | 4 +- .../test_joinRoomScreen-iPad-pseudo.Join.png | 4 +- .../test_joinRoomScreen-iPad-pseudo.Knock.png | 4 +- ...est_joinRoomScreen-iPad-pseudo.Knocked.png | 4 +- ...est_joinRoomScreen-iPad-pseudo.Unknown.png | 4 +- ..._joinRoomScreen-iPhone-16-en-GB.Invite.png | 4 +- ...st_joinRoomScreen-iPhone-16-en-GB.Join.png | 4 +- ...t_joinRoomScreen-iPhone-16-en-GB.Knock.png | 4 +- ...joinRoomScreen-iPhone-16-en-GB.Knocked.png | 4 +- ...joinRoomScreen-iPhone-16-en-GB.Unknown.png | 4 +- ...joinRoomScreen-iPhone-16-pseudo.Invite.png | 4 +- ...t_joinRoomScreen-iPhone-16-pseudo.Join.png | 4 +- ..._joinRoomScreen-iPhone-16-pseudo.Knock.png | 4 +- ...oinRoomScreen-iPhone-16-pseudo.Knocked.png | 4 +- ...oinRoomScreen-iPhone-16-pseudo.Unknown.png | 4 +- UnitTests/Sources/HomeScreenRoomTests.swift | 3 +- .../JoinRoomScreenViewModelTests.swift | 22 +- UnitTests/Sources/LoggingTests.swift | 3 +- UnitTests/Sources/RoomSummaryTests.swift | 3 +- 48 files changed, 492 insertions(+), 120 deletions(-) create mode 100644 ElementX/Sources/Mocks/InvitedRoomProxyMock.swift rename ElementX/Sources/{Screens/RoomScreen/View => Other/SwiftUI/Views}/RoomHeaderView.swift (100%) create mode 100644 ElementX/Sources/Screens/HomeScreen/View/HomeScreenKnockedCell.swift create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenKnockedCell-iPad-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenKnockedCell-iPad-pseudo.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenKnockedCell-iPhone-16-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenKnockedCell-iPhone-16-pseudo.1.png diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 07e8e2e7e0..1d2e485bd4 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -34,7 +34,6 @@ 03CDCA6243F89B194E3FAD17 /* EncryptionAuthenticity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955336CBD5ED73C792D1F580 /* EncryptionAuthenticity.swift */; }; 0437765FF480249486893CC7 /* ScreenTrackerViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 196004E7695FBA292A7944AF /* ScreenTrackerViewModifier.swift */; }; 044DD8F80231BC30570F7965 /* UserDiscoveryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AAD845E53B0C8B5E0812C2 /* UserDiscoveryService.swift */; }; - 04A16B45228F7678A027C079 /* RoomHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 422724361B6555364C43281E /* RoomHeaderView.swift */; }; 04F17DE71A50206336749BAC /* UserPreferenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA241DEEF7C8A7181C0AEDC9 /* UserPreferenceTests.swift */; }; 053B8BD2496207838878C6C9 /* PinnedItemsBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60C9BAE9F9436B14E4E22E8F /* PinnedItemsBannerView.swift */; }; 059173B3C77056C406906B6D /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = D4DA544B2520BFA65D6DB4BB /* target.yml */; }; @@ -46,6 +45,7 @@ 06D3942496E9E0E655F14D21 /* NotificationManagerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A057F2FDC14866C3026A89A4 /* NotificationManagerProtocol.swift */; }; 06F8EDF52E33A2D36BCC1161 /* AppLockScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D6F88FE35A0979D2821E06 /* AppLockScreen.swift */; }; 071A017E415AD378F2961B11 /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 227AC5D71A4CE43512062243 /* URL.swift */; }; + 07376A5274822EB45CC320C7 /* InvitedRoomProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A21027F05874B1BCC3E452B /* InvitedRoomProxyMock.swift */; }; 07756D532EFE33DD1FA258E5 /* GeoURITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7ED2EF5BDBAD2A7DBC4636 /* GeoURITests.swift */; }; 077CB230153E072C94B1E6C3 /* AppAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D65BCC659FD9087E49B3C25 /* AppAppearance.swift */; }; 07CC13C5729C24255348CBBD /* ElementCallWidgetDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 309AD8BAE6437C31BA7157BF /* ElementCallWidgetDriver.swift */; }; @@ -611,6 +611,7 @@ 865DD5CA474C6AE6C2BC008E /* NetworkMonitorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1575947B7A6FE08C57FE5EE4 /* NetworkMonitorProtocol.swift */; }; 86675910612A12409262DFBD /* SessionVerificationStateMachineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1C22B1B5FA3A765EADB2CC9 /* SessionVerificationStateMachineTests.swift */; }; 8691186F9B99BCDDB7CACDD8 /* KeychainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E36CB905A2B9EC2C92A2DA7C /* KeychainController.swift */; }; + 86DFA58FBBEB0AF671D2A1E1 /* HomeScreenKnockedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A103580EBA06155B1343EF16 /* HomeScreenKnockedCell.swift */; }; 86F9D3028A1F4AE819D75560 /* RoomChangePermissionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D879FC4E881E748BB9B34DC /* RoomChangePermissionsScreenCoordinator.swift */; }; 872A6457DF573AF8CEAE927A /* LoginHomeserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9349F590E35CE514A71E6764 /* LoginHomeserver.swift */; }; 874FEFB9D4A4AF447E0E086E /* AuthenticationStartScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0F7CCC4A9D1927223F559D5 /* AuthenticationStartScreenViewModelProtocol.swift */; }; @@ -716,6 +717,7 @@ 9BD3A773186291560DF92B62 /* RoomTimelineProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F2402D738694F98729A441 /* RoomTimelineProvider.swift */; }; 9C4EC28A921486B1775D7F8C /* IdentityConfirmedScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 307702DD66E7DDCDD9214784 /* IdentityConfirmedScreen.swift */; }; 9C55746D8F6A3E35CFCF4A7A /* AuthenticationStartLogo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 598F01EBD0C4CC550C644418 /* AuthenticationStartLogo.swift */; }; + 9C63171267E22FEB288EC860 /* RoomHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1627F2D56477BD331F6D732C /* RoomHeaderView.swift */; }; 9CBB04365408F9D6F46BA3A7 /* PinnedEventsTimelineFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A54AAF72E821B4084B7E4298 /* PinnedEventsTimelineFlowCoordinator.swift */; }; 9D2E03DB175A6AB14589076D /* AnalyticsEvents in Frameworks */ = {isa = PBXBuildFile; productRef = 2A3F7BCCB18C15B30CCA39A9 /* AnalyticsEvents */; }; 9D79B94493FB32249F7E472F /* PlaceholderAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */; }; @@ -1301,6 +1303,7 @@ 1575947B7A6FE08C57FE5EE4 /* NetworkMonitorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitorProtocol.swift; sourceTree = ""; }; 15A657D96779D1DEB8EF1327 /* CreateRoomViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomViewModel.swift; sourceTree = ""; }; 161CD412E75F4086F422AE39 /* SessionVerificationScreenStateMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreenStateMachine.swift; sourceTree = ""; }; + 1627F2D56477BD331F6D732C /* RoomHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomHeaderView.swift; sourceTree = ""; }; 16D09C79746BDCD9173EB3A7 /* RoomDetailsEditScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreenModels.swift; sourceTree = ""; }; 1715E3D7F53C0748AA50C91C /* PostHogAnalyticsClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogAnalyticsClient.swift; sourceTree = ""; }; 1734A445A58ED855B977A0A8 /* TracingConfigurationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracingConfigurationTests.swift; sourceTree = ""; }; @@ -1465,6 +1468,7 @@ 398817652FA8ABAE0A31AC6D /* ReadableFrameModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadableFrameModifier.swift; sourceTree = ""; }; 39C0D861FC397AC34BCF089E /* KeychainControllerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainControllerMock.swift; sourceTree = ""; }; 3A12D3D8138F1B71AFA7C858 /* CompletionSuggestionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletionSuggestionService.swift; sourceTree = ""; }; + 3A21027F05874B1BCC3E452B /* InvitedRoomProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitedRoomProxyMock.swift; sourceTree = ""; }; 3AD253E7EFF88F308D644272 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/SAS.strings"; sourceTree = ""; }; 3B5E97E9615A158C76B2AB77 /* DateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTests.swift; sourceTree = ""; }; 3BAC027034248429A438886B /* AppMediatorMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediatorMock.swift; sourceTree = ""; }; @@ -1504,7 +1508,6 @@ 421E716C521F96D24ECE69B3 /* NoticeRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineItem.swift; sourceTree = ""; }; 421FA93BCC2840E66E4F306F /* NotificationSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsScreenViewModelProtocol.swift; sourceTree = ""; }; 42236480CF0431535EBE8387 /* CustomLayoutLabelStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomLayoutLabelStyle.swift; sourceTree = ""; }; - 422724361B6555364C43281E /* RoomHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomHeaderView.swift; sourceTree = ""; }; 42C64A14EE89928207E3B42B /* AnalyticsSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenModels.swift; sourceTree = ""; }; 42C8C368A611B9CB79C7F5FA /* RoomPollsHistoryScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreen.swift; sourceTree = ""; }; 436A0D98D372B17EAE9AA999 /* GlobalSearchScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalSearchScreenModels.swift; sourceTree = ""; }; @@ -1901,6 +1904,7 @@ A019A12C866D64CF072024B9 /* AppLockSetupPINScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenViewModel.swift; sourceTree = ""; }; A02D1A490944BF01A37586E1 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/SAS.strings; sourceTree = ""; }; A057F2FDC14866C3026A89A4 /* NotificationManagerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManagerProtocol.swift; sourceTree = ""; }; + A103580EBA06155B1343EF16 /* HomeScreenKnockedCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenKnockedCell.swift; sourceTree = ""; }; A12D3B1BCF920880CA8BBB6B /* UserIndicatorControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorControllerProtocol.swift; sourceTree = ""; }; A130A2251A15A7AACC84FD37 /* RoomPollsHistoryScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreenViewModelProtocol.swift; sourceTree = ""; }; A16CD2C62CB7DB78A4238485 /* ReportContentScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentScreenCoordinator.swift; sourceTree = ""; }; @@ -2915,6 +2919,7 @@ 4E600B315B920B9687F8EE1B /* ComposerDraftServiceMock.swift */, E321E840DCC63790049984F4 /* ElementCallServiceMock.swift */, 1B10423B9102086A2D9BFCBA /* EventTimelineItem.swift */, + 3A21027F05874B1BCC3E452B /* InvitedRoomProxyMock.swift */, 867DC9530C42F7B5176BE465 /* JoinedRoomProxyMock.swift */, 9E8F4D7D61B80EBD5CB92F8A /* KnockedRoomProxyMock.swift */, 6F65E4BB9E82EB8373207CF8 /* MediaProviderMock.swift */, @@ -2964,6 +2969,7 @@ 648DD1C10E4957CB791FE0B8 /* OverridableAvatarImage.swift */, C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */, BEF5FE93A06F563B477F024A /* RoomAvatarImage.swift */, + 1627F2D56477BD331F6D732C /* RoomHeaderView.swift */, 7EB58E4E8D6D634C246AD5C2 /* RoomInviterLabel.swift */, 839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */, DE7C80EF77AD102053D3646E /* RoundedLabelItem.swift */, @@ -3425,6 +3431,7 @@ A3B4B58B79A6FA250B24A1EC /* HomeScreenContent.swift */, C0FEA560929DD73FFEF8C3DF /* HomeScreenEmptyStateView.swift */, D8FC33C3F6BF597E095CE9FA /* HomeScreenInviteCell.swift */, + A103580EBA06155B1343EF16 /* HomeScreenKnockedCell.swift */, 05512FB13987D221B7205DE0 /* HomeScreenRecoveryKeyConfirmationBanner.swift */, ED044D00F2176681CC02CD54 /* HomeScreenRoomCell.swift */, C7661EFFCAA307A97D71132A /* HomeScreenRoomList.swift */, @@ -4036,7 +4043,6 @@ 79023E5904B155E8E2B8B502 /* View */ = { isa = PBXGroup; children = ( - 422724361B6555364C43281E /* RoomHeaderView.swift */, 5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */, 4137900E28201C314C835C11 /* RoomScreenFooterView.swift */, 4552D3466B1453F287223ADA /* SwipeRightAction.swift */, @@ -6501,6 +6507,7 @@ 8CC12086CBF91A7E10CDC205 /* HomeScreenCoordinator.swift in Sources */, 77BB228AEA861E50FFD6A228 /* HomeScreenEmptyStateView.swift in Sources */, 22C5483D01EEB290B8339817 /* HomeScreenInviteCell.swift in Sources */, + 86DFA58FBBEB0AF671D2A1E1 /* HomeScreenKnockedCell.swift in Sources */, 8810A2A30A68252EBB54EE05 /* HomeScreenModels.swift in Sources */, B04E9EB589CE99C3929E817A /* HomeScreenRecoveryKeyConfirmationBanner.swift in Sources */, 0AE0AB1952F186EB86719B4F /* HomeScreenRoomCell.swift in Sources */, @@ -6532,6 +6539,7 @@ F519DE17A3A0F760307B2E6D /* InviteUsersScreenViewModel.swift in Sources */, A17FAD2EBC53E17B5FD384DB /* InviteUsersScreenViewModelProtocol.swift in Sources */, 89B909AC66B96FA054EF3C14 /* InvitedRoomProxy.swift in Sources */, + 07376A5274822EB45CC320C7 /* InvitedRoomProxyMock.swift in Sources */, 6A54F52443EC52AC5CD772C0 /* JoinRoomScreen.swift in Sources */, AFE2AB612A1460E49578D746 /* JoinRoomScreenCoordinator.swift in Sources */, DEDBD3E9CFCC9F20CAC79881 /* JoinRoomScreenModels.swift in Sources */, @@ -6770,7 +6778,7 @@ 2814E7075BF3A5C0CCBC9F90 /* RoomDirectorySearchView.swift in Sources */, 42F1C8731166633E35A6D7E6 /* RoomEventStringBuilder.swift in Sources */, D55AF9B5B55FEED04771A461 /* RoomFlowCoordinator.swift in Sources */, - 04A16B45228F7678A027C079 /* RoomHeaderView.swift in Sources */, + 9C63171267E22FEB288EC860 /* RoomHeaderView.swift in Sources */, 8A83D715940378B9BA9F739E /* RoomInviterLabel.swift in Sources */, F4996C82A4B3A5FF0C8EDD03 /* RoomListFilterModels.swift in Sources */, 4A9CEEE612D6D8B3DDBD28BA /* RoomListFilterView.swift in Sources */, diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 6e49740a0e..4aeb3bef7b 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -371,6 +371,7 @@ "screen_room_pinned_banner_loading_description" = "Loading message…"; "screen_room_pinned_banner_view_all_button_title" = "View All"; "screen_room_details_pinned_events_row_title" = "Pinned messages"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; @@ -822,12 +823,16 @@ "screen_session_verification_open_existing_session_title" = "Open an existing session"; "screen_session_verification_positive_button_canceled" = "Retry verification"; "screen_session_verification_positive_button_initial" = "I am ready"; -"screen_session_verification_positive_button_verifying_ongoing" = "Waiting to match"; +"screen_session_verification_positive_button_verifying_ongoing" = "Waiting to match…"; "screen_session_verification_ready_subtitle" = "Compare a unique set of emojis."; "screen_session_verification_request_accepted_subtitle" = "Compare the unique emoji, ensuring they appear in the same order."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "They don’t match"; "screen_session_verification_they_match" = "They match"; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 2439a43468..a9fef6619b 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -1965,6 +1965,8 @@ internal enum L10n { /// Congrats! /// You don’t have any unread messages! internal static var screenRoomlistFilterUnreadsEmptyStateTitle: String { return L10n.tr("Localizable", "screen_roomlist_filter_unreads_empty_state_title") } + /// Request to join sent + internal static var screenRoomlistKnockEventSentDescription: String { return L10n.tr("Localizable", "screen_roomlist_knock_event_sent_description") } /// Chats internal static var screenRoomlistMainSpaceTitle: String { return L10n.tr("Localizable", "screen_roomlist_main_space_title") } /// Mark as read @@ -2013,7 +2015,7 @@ internal enum L10n { internal static var screenSessionVerificationPositiveButtonCanceled: String { return L10n.tr("Localizable", "screen_session_verification_positive_button_canceled") } /// I am ready internal static var screenSessionVerificationPositiveButtonInitial: String { return L10n.tr("Localizable", "screen_session_verification_positive_button_initial") } - /// Waiting to match + /// Waiting to match… internal static var screenSessionVerificationPositiveButtonVerifyingOngoing: String { return L10n.tr("Localizable", "screen_session_verification_positive_button_verifying_ongoing") } /// Compare a unique set of emojis. internal static var screenSessionVerificationReadySubtitle: String { return L10n.tr("Localizable", "screen_session_verification_ready_subtitle") } @@ -2021,10 +2023,18 @@ internal enum L10n { internal static var screenSessionVerificationRequestAcceptedSubtitle: String { return L10n.tr("Localizable", "screen_session_verification_request_accepted_subtitle") } /// Signed in internal static var screenSessionVerificationRequestDetailsTimestamp: String { return L10n.tr("Localizable", "screen_session_verification_request_details_timestamp") } + /// Either the request timed out, the request was denied, or there was a verification mismatch. + internal static var screenSessionVerificationRequestFailureSubtitle: String { return L10n.tr("Localizable", "screen_session_verification_request_failure_subtitle") } + /// Verification failed + internal static var screenSessionVerificationRequestFailureTitle: String { return L10n.tr("Localizable", "screen_session_verification_request_failure_title") } /// Only continue if you initiated this verification. internal static var screenSessionVerificationRequestFooter: String { return L10n.tr("Localizable", "screen_session_verification_request_footer") } /// Verify the other device to keep your message history secure. internal static var screenSessionVerificationRequestSubtitle: String { return L10n.tr("Localizable", "screen_session_verification_request_subtitle") } + /// Now you can read or send messages securely on your other device. + internal static var screenSessionVerificationRequestSuccessSubtitle: String { return L10n.tr("Localizable", "screen_session_verification_request_success_subtitle") } + /// Device verified + internal static var screenSessionVerificationRequestSuccessTitle: String { return L10n.tr("Localizable", "screen_session_verification_request_success_title") } /// Verification requested internal static var screenSessionVerificationRequestTitle: String { return L10n.tr("Localizable", "screen_session_verification_request_title") } /// They don’t match diff --git a/ElementX/Sources/Mocks/InvitedRoomProxyMock.swift b/ElementX/Sources/Mocks/InvitedRoomProxyMock.swift new file mode 100644 index 0000000000..5155a6febd --- /dev/null +++ b/ElementX/Sources/Mocks/InvitedRoomProxyMock.swift @@ -0,0 +1,31 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Combine +import Foundation + +@MainActor +struct InvitedRoomProxyMockConfiguration { + var id = UUID().uuidString + var name: String? + var avatarURL: URL? + var members: [RoomMemberProxyMock] = .allMembers + var inviter: RoomMemberProxyMock = .mockAlice +} + +extension InvitedRoomProxyMock { + @MainActor + convenience init(_ configuration: InvitedRoomProxyMockConfiguration) { + self.init() + id = configuration.id + name = configuration.name + avatarURL = configuration.avatarURL + avatar = .room(id: configuration.id, name: configuration.name, avatarURL: configuration.avatarURL) // Note: This doesn't replicate the real proxy logic. + underlyingInviter = configuration.inviter + activeMembersCount = configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count + } +} diff --git a/ElementX/Sources/Mocks/RoomSummaryProviderMock.swift b/ElementX/Sources/Mocks/RoomSummaryProviderMock.swift index 7e47548e0f..ed88e9efe2 100644 --- a/ElementX/Sources/Mocks/RoomSummaryProviderMock.swift +++ b/ElementX/Sources/Mocks/RoomSummaryProviderMock.swift @@ -71,8 +71,7 @@ extension Array where Element == RoomSummary { static let mockRooms: [Element] = [ RoomSummary(roomListItem: RoomListItemSDKMock(), id: "1", - isInvite: false, - inviter: nil, + joinRequestType: nil, name: "Foundation 🔭🪐🌌", isDirect: false, avatarURL: nil, @@ -89,8 +88,7 @@ extension Array where Element == RoomSummary { isFavourite: false), RoomSummary(roomListItem: RoomListItemSDKMock(), id: "2", - isInvite: false, - inviter: nil, + joinRequestType: nil, name: "Foundation and Empire", isDirect: false, avatarURL: URL.picturesDirectory, @@ -107,8 +105,7 @@ extension Array where Element == RoomSummary { isFavourite: false), RoomSummary(roomListItem: RoomListItemSDKMock(), id: "3", - isInvite: false, - inviter: nil, + joinRequestType: nil, name: "Second Foundation", isDirect: false, avatarURL: nil, @@ -125,8 +122,7 @@ extension Array where Element == RoomSummary { isFavourite: false), RoomSummary(roomListItem: RoomListItemSDKMock(), id: "4", - isInvite: false, - inviter: nil, + joinRequestType: nil, name: "Foundation's Edge", isDirect: false, avatarURL: nil, @@ -143,8 +139,7 @@ extension Array where Element == RoomSummary { isFavourite: false), RoomSummary(roomListItem: RoomListItemSDKMock(), id: "5", - isInvite: false, - inviter: nil, + joinRequestType: nil, name: "Foundation and Earth", isDirect: true, avatarURL: nil, @@ -161,8 +156,7 @@ extension Array where Element == RoomSummary { isFavourite: false), RoomSummary(roomListItem: RoomListItemSDKMock(), id: "6", - isInvite: false, - inviter: nil, + joinRequestType: nil, name: "Prelude to Foundation", isDirect: true, avatarURL: nil, @@ -179,8 +173,7 @@ extension Array where Element == RoomSummary { isFavourite: false), RoomSummary(roomListItem: RoomListItemSDKMock(), id: "0", - isInvite: false, - inviter: nil, + joinRequestType: nil, name: "Unknown", isDirect: false, avatarURL: nil, @@ -230,8 +223,7 @@ extension Array where Element == RoomSummary { static let mockInvites: [Element] = [ RoomSummary(roomListItem: RoomListItemSDKMock(), id: "someAwesomeRoomId1", - isInvite: false, - inviter: RoomMemberProxyMock.mockCharlie, + joinRequestType: .invite(inviter: RoomMemberProxyMock.mockCharlie), name: "First room", isDirect: false, avatarURL: URL.picturesDirectory, @@ -248,8 +240,7 @@ extension Array where Element == RoomSummary { isFavourite: false), RoomSummary(roomListItem: RoomListItemSDKMock(), id: "someAwesomeRoomId2", - isInvite: false, - inviter: RoomMemberProxyMock.mockCharlie, + joinRequestType: .invite(inviter: RoomMemberProxyMock.mockCharlie), name: "Second room", isDirect: true, avatarURL: nil, diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomHeaderView.swift b/ElementX/Sources/Other/SwiftUI/Views/RoomHeaderView.swift similarity index 100% rename from ElementX/Sources/Screens/RoomScreen/View/RoomHeaderView.swift rename to ElementX/Sources/Other/SwiftUI/Views/RoomHeaderView.swift diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift index 96106f1ea9..d253a14be5 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift @@ -127,10 +127,11 @@ struct HomeScreenViewStateBindings { } struct HomeScreenRoom: Identifiable, Equatable { - enum RoomType { + enum RoomType: Equatable { case placeholder case room - case invite + case invite(inviterDetails: RoomInviterDetails?) + case knock } static let placeholderLastMessage = AttributedString("Hidden last message") @@ -143,6 +144,13 @@ struct HomeScreenRoom: Identifiable, Equatable { let type: RoomType + var inviter: RoomInviterDetails? { + if case .invite(let inviter) = type { + return inviter + } + return nil + } + let badges: Badges struct Badges: Equatable { let isDotShown: Bool @@ -164,9 +172,7 @@ struct HomeScreenRoom: Identifiable, Equatable { let lastMessage: AttributedString? let avatar: RoomAvatar - - let inviter: RoomInviterDetails? - + let canonicalAlias: String? static func placeholder() -> HomeScreenRoom { @@ -181,7 +187,6 @@ struct HomeScreenRoom: Identifiable, Equatable { timestamp: "Now", lastMessage: placeholderLastMessage, avatar: .room(id: "", name: "", avatarURL: nil), - inviter: nil, canonicalAlias: nil) } } @@ -192,17 +197,21 @@ extension HomeScreenRoom { let hasUnreadMessages = hideUnreadMessagesBadge ? false : summary.hasUnreadMessages - let isDotShown = hasUnreadMessages || summary.hasUnreadMentions || summary.hasUnreadNotifications || summary.isMarkedUnread + let isDotShown = hasUnreadMessages || summary.hasUnreadMentions || summary.hasUnreadNotifications || summary.isMarkedUnread || summary.joinRequestType?.isKnock == true let isMentionShown = summary.hasUnreadMentions && !summary.isMuted let isMuteShown = summary.isMuted let isCallShown = summary.hasOngoingCall - let isHighlighted = summary.isMarkedUnread || (!summary.isMuted && (summary.hasUnreadNotifications || summary.hasUnreadMentions)) + let isHighlighted = summary.isMarkedUnread || (!summary.isMuted && (summary.hasUnreadNotifications || summary.hasUnreadMentions)) || summary.joinRequestType?.isKnock == true - let inviter = summary.inviter.map(RoomInviterDetails.init) + let type: HomeScreenRoom.RoomType = switch summary.joinRequestType { + case .invite(let inviter): .invite(inviterDetails: inviter.map(RoomInviterDetails.init)) + case .knock: .knock + case .none: .room + } self.init(id: identifier, roomID: summary.id, - type: summary.isInvite ? .invite : .room, + type: type, badges: .init(isDotShown: isDotShown, isMentionShown: isMentionShown, isMuteShown: isMuteShown, @@ -214,7 +223,6 @@ extension HomeScreenRoom { timestamp: summary.lastMessageFormattedTimestamp, lastMessage: summary.lastMessage, avatar: summary.avatar, - inviter: inviter, canonicalAlias: summary.canonicalAlias) } } diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenInviteCell.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenInviteCell.swift index aaf7ca0fad..2abc3a7701 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenInviteCell.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenInviteCell.swift @@ -69,7 +69,8 @@ struct HomeScreenInviteCell: View { @ViewBuilder private var inviterView: some View { - if let inviter = room.inviter, !room.isDirect { + if let inviter = room.inviter, + !room.isDirect { RoomInviterLabel(inviter: inviter, mediaProvider: context.mediaProvider) .font(.compound.bodyMD) .foregroundStyle(.compound.textPlaceholder) @@ -177,8 +178,7 @@ private extension HomeScreenRoom { let summary = RoomSummary(roomListItem: RoomListItemSDKMock(), id: "@someone:somewhere.com", - isInvite: false, - inviter: inviter, + joinRequestType: .invite(inviter: inviter), name: "Some Guy", isDirect: true, avatarURL: nil, @@ -205,8 +205,7 @@ private extension HomeScreenRoom { let summary = RoomSummary(roomListItem: RoomListItemSDKMock(), id: "@someone:somewhere.com", - isInvite: false, - inviter: inviter, + joinRequestType: .invite(inviter: inviter), name: "Awesome Room", isDirect: false, avatarURL: avatarURL, diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenKnockedCell.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenKnockedCell.swift new file mode 100644 index 0000000000..a1cdf9bfb4 --- /dev/null +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenKnockedCell.swift @@ -0,0 +1,200 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Combine +import Compound +import SwiftUI + +@MainActor +struct HomeScreenKnockedCell: View { + @Environment(\.dynamicTypeSize) var dynamicTypeSize + + let room: HomeScreenRoom + let context: HomeScreenViewModel.Context + + var body: some View { + HStack(alignment: .top, spacing: 16) { + if dynamicTypeSize < .accessibility3 { + RoomAvatarImage(avatar: room.avatar, + avatarSize: .custom(52), + mediaProvider: context.mediaProvider) + .dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1) + .accessibilityHidden(true) + } + + mainContent + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.bottom, 16) + .padding(.trailing, 16) + .multilineTextAlignment(.leading) + .overlay(alignment: .bottom) { + separator + } + } + .padding(.top, 12) + .padding(.leading, 16) + .onTapGesture { + if let roomID = room.roomID { + context.send(viewAction: .selectRoom(roomIdentifier: roomID)) + } + } + } + + // MARK: - Private + + private var mainContent: some View { + VStack(alignment: .leading, spacing: 0) { + VStack(alignment: .leading, spacing: 0) { + HStack(alignment: .firstTextBaseline, spacing: 16) { + textualContent + badge + } + + Text(L10n.screenRoomlistKnockEventSentDescription) + .font(.compound.bodyMD) + .foregroundStyle(.compound.textPlaceholder) + .padding(.top, room.canonicalAlias == nil ? 0 : 4) + .padding(.trailing, 16) + } + .fixedSize(horizontal: false, vertical: true) + .accessibilityElement(children: .combine) + } + } + + @ViewBuilder + private var textualContent: some View { + VStack(alignment: .leading, spacing: 0) { + Text(title) + .font(.compound.bodyLGSemibold) + .foregroundColor(.compound.textPrimary) + .lineLimit(2) + + if let subtitle { + Text(subtitle) + .font(.compound.bodyMD) + .foregroundColor(.compound.textPlaceholder) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + } + + private var separator: some View { + Rectangle() + .fill(Color.compound.borderDisabled) + .frame(height: 1 / UIScreen.main.scale) + } + + private var title: String { + room.name + } + + private var subtitle: String? { + room.canonicalAlias + } + + private var badge: some View { + Circle() + .scaledFrame(size: 12) + .foregroundColor(.compound.iconAccentTertiary) + } +} + +struct HomeScreenKnockedCell_Previews: PreviewProvider, TestablePreview { + static var previews: some View { + ScrollView { + VStack(spacing: 0) { + HomeScreenKnockedCell(room: .dmInvite, + context: viewModel().context) + + HomeScreenKnockedCell(room: .dmInvite, + context: viewModel().context) + + HomeScreenKnockedCell(room: .roomKnocked(), + context: viewModel().context) + + HomeScreenKnockedCell(room: .roomKnocked(), + context: viewModel().context) + + HomeScreenKnockedCell(room: .roomKnocked(alias: "#footest:somewhere.org", avatarURL: .picturesDirectory), + context: viewModel().context) + + HomeScreenKnockedCell(room: .roomKnocked(alias: "#footest:somewhere.org"), + context: viewModel().context) + .dynamicTypeSize(.accessibility1) + .previewDisplayName("Aliased room (AX1)") + } + } + } + + static func viewModel() -> HomeScreenViewModel { + let clientProxy = ClientProxyMock(.init()) + + let userSession = UserSessionMock(.init(clientProxy: clientProxy)) + + return HomeScreenViewModel(userSession: userSession, + analyticsService: ServiceLocator.shared.analytics, + appSettings: ServiceLocator.shared.settings, + selectedRoomPublisher: CurrentValueSubject(nil).asCurrentValuePublisher(), + userIndicatorController: ServiceLocator.shared.userIndicatorController) + } +} + +@MainActor +private extension HomeScreenRoom { + static var dmInvite: HomeScreenRoom { + let inviter = RoomMemberProxyMock() + inviter.displayName = "Jack" + inviter.userID = "@jack:somewhere.com" + + let summary = RoomSummary(roomListItem: RoomListItemSDKMock(), + id: "@someone:somewhere.com", + joinRequestType: .invite(inviter: inviter), + name: "Some Guy", + isDirect: true, + avatarURL: nil, + heroes: [.init(userID: "@someone:somewhere.com")], + lastMessage: nil, + lastMessageFormattedTimestamp: nil, + unreadMessagesCount: 0, + unreadMentionsCount: 0, + unreadNotificationsCount: 0, + notificationMode: nil, + canonicalAlias: "#footest:somewhere.org", + hasOngoingCall: false, + isMarkedUnread: false, + isFavourite: false) + + return .init(summary: summary, hideUnreadMessagesBadge: false) + } + + static func roomKnocked(alias: String? = nil, avatarURL: URL? = nil) -> HomeScreenRoom { + let inviter = RoomMemberProxyMock() + inviter.displayName = "Luca" + inviter.userID = "@jack:somewhi.nl" + inviter.avatarURL = avatarURL + + let summary = RoomSummary(roomListItem: RoomListItemSDKMock(), + id: "@someone:somewhere.com", + joinRequestType: .invite(inviter: inviter), + name: "Awesome Room", + isDirect: false, + avatarURL: avatarURL, + heroes: [.init(userID: "@someone:somewhere.com")], + lastMessage: nil, + lastMessageFormattedTimestamp: nil, + unreadMessagesCount: 0, + unreadMentionsCount: 0, + unreadNotificationsCount: 0, + notificationMode: nil, + canonicalAlias: alias, + hasOngoingCall: false, + isMarkedUnread: false, + isFavourite: false) + + return .init(summary: summary, hideUnreadMessagesBadge: false) + } +} diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift index 393b5c7227..fb224413a2 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift @@ -32,6 +32,8 @@ struct HomeScreenRoomList: View { .redacted(reason: .placeholder) case .invite: HomeScreenInviteCell(room: room, context: context) + case .knock: + HomeScreenKnockedCell(room: room, context: context) case .room: let isSelected = context.viewState.selectedRoomID == room.id diff --git a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenCoordinator.swift b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenCoordinator.swift index fbe2e23c0d..d259e53e1b 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenCoordinator.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenCoordinator.swift @@ -49,7 +49,7 @@ final class JoinRoomScreenCoordinator: CoordinatorProtocol { switch action { case .joined: actionsSubject.send(.joined) - case .cancelled: + case .dismiss: actionsSubject.send(.cancelled) } } diff --git a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenModels.swift b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenModels.swift index a0bf1d91c6..5def712723 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenModels.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenModels.swift @@ -9,7 +9,7 @@ import Foundation enum JoinRoomScreenViewModelAction { case joined - case cancelled + case dismiss } enum JoinRoomScreenInteractionMode { @@ -65,6 +65,7 @@ struct JoinRoomScreenViewStateBindings { enum JoinRoomScreenAlertType { case declineInvite + case cancelKnock } enum JoinRoomScreenViewAction { diff --git a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift index 74e7a69666..b97b85bd7e 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift @@ -59,8 +59,7 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo case .declineInvite: showDeclineInviteConfirmationAlert() case .cancelKnock: - // TODO: implement once available - break + showCancelKnockConfirmationAlert() } } @@ -78,15 +77,7 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo Task { await updateRoomDetails() } } - // Using only the preview API isn't enough as it's not capable - // of giving us information for non-joined rooms (at least not on synapse) - // See if we known about the room locally and, if so, have that - // take priority over the preview one. - - if let room = await clientProxy.roomForIdentifier(roomID) { - self.room = room - await updateRoomDetails() - } + await updateRoom() switch await clientProxy.roomPreviewForIdentifier(roomID, via: via) { case .success(let roomPreviewDetails): @@ -99,6 +90,17 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo } } + private func updateRoom() async { + // Using only the preview API isn't enough as it's not capable + // of giving us information for non-joined rooms (at least not on synapse) + // See if we known about the room locally and, if so, have that + // take priority over the preview one. + if let room = await clientProxy.roomForIdentifier(roomID) { + self.room = room + await updateRoomDetails() + } + } + private func updateRoomDetails() async { var roomProxy: RoomProxyProtocol? var inviter: RoomInviterDetails? @@ -188,7 +190,8 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo switch await clientProxy.knockRoomAlias(alias, message: state.bindings.knockMessage.isBlank ? nil : state.bindings.knockMessage) { case .success: - state.mode = .knocked + // The room should become knocked through the sync + await updateRoom() case .failure(let error): MXLog.error("Failed knocking room alias: \(alias) with error: \(error)") userIndicatorController.submitIndicator(.init(title: L10n.errorUnknown)) @@ -198,7 +201,8 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo via: via, message: state.bindings.knockMessage.isBlank ? nil : state.bindings.knockMessage) { case .success: - state.mode = .knocked + // The room should become knocked through the sync + await updateRoom() case .failure(let error): MXLog.error("Failed knocking room id: \(roomID) with error: \(error)") userIndicatorController.submitIndicator(.init(title: L10n.errorUnknown)) @@ -220,6 +224,14 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo secondaryButton: .init(title: L10n.actionDecline, role: .destructive, action: { Task { await self.declineInvite() } })) } + private func showCancelKnockConfirmationAlert() { + state.bindings.alertInfo = .init(id: .cancelKnock, + title: L10n.screenJoinRoomCancelKnockAlertTitle, + message: L10n.screenJoinRoomCancelKnockAlertDescription, + primaryButton: .init(title: L10n.actionNo, role: .cancel, action: nil), + secondaryButton: .init(title: L10n.screenJoinRoomCancelKnockAlertConfirmation, role: .destructive, action: { Task { await self.cancelKnock() } })) + } + private func declineInvite() async { defer { userIndicatorController.retractIndicatorWithId(roomID) @@ -236,6 +248,29 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo if case .failure = result { userIndicatorController.submitIndicator(.init(title: L10n.errorUnknown)) + } else { + actionsSubject.send(.dismiss) + } + } + + private func cancelKnock() async { + defer { + userIndicatorController.retractIndicatorWithId(roomID) + } + + userIndicatorController.submitIndicator(UserIndicator(id: roomID, type: .modal, title: L10n.commonLoading, persistent: true)) + + guard case let .knocked(roomProxy) = room else { + userIndicatorController.submitIndicator(.init(title: L10n.errorUnknown)) + return + } + + let result = await roomProxy.cancelKnock() + + if case .failure = result { + userIndicatorController.submitIndicator(.init(title: L10n.errorUnknown)) + } else { + actionsSubject.send(.dismiss) } } diff --git a/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift b/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift index 419e463297..f47e63c632 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift @@ -14,7 +14,7 @@ struct JoinRoomScreen: View { @ObservedObject var context: JoinRoomScreenViewModel.Context var body: some View { - FullscreenDialog(topPadding: 80, background: .bloom) { + FullscreenDialog(topPadding: context.viewState.mode == .knocked ? 151 : 35, background: .bloom) { if context.viewState.mode == .loading { EmptyView() } else { @@ -27,13 +27,15 @@ struct JoinRoomScreen: View { .background() .backgroundStyle(.compound.bgCanvasDefault) .navigationBarTitleDisplayMode(.inline) + .toolbar { toolbar } } @ViewBuilder var mainContent: some View { - if context.viewState.mode == .knocked { + switch context.viewState.mode { + case .knocked: knockedView - } else { + default: defaultView } } @@ -54,7 +56,7 @@ struct JoinRoomScreen: View { if let subtitle = context.viewState.subtitle { Text(subtitle) - .font(.compound.bodyMD) + .font(.compound.bodyLG) .foregroundStyle(.compound.textSecondary) .multilineTextAlignment(.center) } @@ -101,7 +103,7 @@ struct JoinRoomScreen: View { } } } - + @ViewBuilder private var knockMessage: some View { VStack(alignment: .leading, spacing: 12) { @@ -158,6 +160,17 @@ struct JoinRoomScreen: View { Button(L10n.actionAccept) { context.send(viewAction: .acceptInvite) } .buttonStyle(.compound(.primary)) } + + @ToolbarContentBuilder + private var toolbar: some ToolbarContent { + if context.viewState.mode == .knocked { + ToolbarItem(placement: .principal) { + RoomHeaderView(roomName: context.viewState.title, + roomAvatar: context.viewState.avatar, + mediaProvider: context.mediaProvider) + } + } + } } // MARK: - Previews @@ -221,10 +234,17 @@ struct JoinRoomScreen_Previews: PreviewProvider, TestablePreview { if mode == .unknown { clientProxy.roomPreviewForIdentifierViaReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) } else { - if mode == .knocked { + switch mode { + case .knocked: + clientProxy.roomForIdentifierClosure = { _ in + .knocked(KnockedRoomProxyMock(.init(avatarURL: URL.homeDirectory))) + } + case .invited: clientProxy.roomForIdentifierClosure = { _ in - .knocked(KnockedRoomProxyMock(.init())) + .invited(InvitedRoomProxyMock(.init(avatarURL: URL.homeDirectory))) } + default: + break } clientProxy.roomPreviewForIdentifierViaReturnValue = .success(.init(roomID: "1", name: "The Three-Body Problem - 三体", diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 643b8844ef..0bba67ad5e 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -417,7 +417,7 @@ class ClientProxy: ClientProxyProtocol { func knockRoom(_ roomID: String, via: [String], message: String?) async -> Result { do { - let _ = try await client.knock(roomIdOrAlias: roomID, reason: nil, serverNames: via) + let _ = try await client.knock(roomIdOrAlias: roomID, reason: message, serverNames: via) await waitForRoomToSync(roomID: roomID, timeout: .seconds(30)) return .success(()) } catch { @@ -428,7 +428,7 @@ class ClientProxy: ClientProxyProtocol { func knockRoomAlias(_ roomAlias: String, message: String?) async -> Result { do { - let room = try await client.knock(roomIdOrAlias: roomAlias, reason: nil, serverNames: []) + let room = try await client.knock(roomIdOrAlias: roomAlias, reason: message, serverNames: []) await waitForRoomToSync(roomID: room.id(), timeout: .seconds(30)) return .success(()) } catch { diff --git a/ElementX/Sources/Services/Room/KnockedRoomProxy.swift b/ElementX/Sources/Services/Room/KnockedRoomProxy.swift index 942baa0412..7ca349f331 100644 --- a/ElementX/Sources/Services/Room/KnockedRoomProxy.swift +++ b/ElementX/Sources/Services/Room/KnockedRoomProxy.swift @@ -76,7 +76,11 @@ class KnockedRoomProxy: KnockedRoomProxyProtocol { } func cancelKnock() async -> Result { - // TODO: Implement this once the API is available - .failure(.invalidURL) + do { + return try await .success(room.leave()) + } catch { + MXLog.error("Failed cancelling the knock with error: \(error)") + return .failure(.sdkError(error)) + } } } diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummary.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummary.swift index 942a78704f..429cba58ad 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummary.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummary.swift @@ -9,12 +9,32 @@ import Foundation import MatrixRustSDK struct RoomSummary { + enum JoinRequestType { + case invite(inviter: RoomMemberProxyProtocol?) + case knock + + var isInvite: Bool { + if case .invite = self { + return true + } else { + return false + } + } + + var isKnock: Bool { + if case .knock = self { + return true + } else { + return false + } + } + } + let roomListItem: RoomListItem let id: String - let isInvite: Bool - let inviter: RoomMemberProxyProtocol? + let joinRequestType: JoinRequestType? let name: String let isDirect: Bool @@ -67,10 +87,9 @@ extension RoomSummary { unreadNotificationsCount = hasUnreadNotifications ? 1 : 0 notificationMode = settingsMode canonicalAlias = nil - inviter = nil hasOngoingCall = false - isInvite = false + joinRequestType = nil isMarkedUnread = false isFavourite = false } diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift index db51b77c8e..4622c5661e 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift @@ -255,10 +255,15 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { let notificationMode = roomInfo.cachedUserDefinedNotificationMode.flatMap { RoomNotificationModeProxy.from(roomNotificationMode: $0) } + let joinRequestType: RoomSummary.JoinRequestType? = switch roomInfo.membership { + case .invited: .invite(inviter: inviterProxy) + case .knocked: .knock + default: nil + } + return RoomSummary(roomListItem: roomListItem, id: roomInfo.id, - isInvite: roomInfo.membership == .invited, - inviter: inviterProxy, + joinRequestType: joinRequestType, name: roomInfo.displayName ?? roomInfo.id, isDirect: roomInfo.isDirect, avatarURL: roomInfo.avatarUrl.flatMap(URL.init(string:)), diff --git a/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift index 9301314132..e1c8b5f6fe 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift @@ -18,7 +18,6 @@ enum TimelineKind { enum TimelineProxyError: Error { case sdkError(Error) - case failedEditing case failedRedacting case failedPaginatingEndReached } diff --git a/PreviewTests/Sources/GeneratedPreviewTests.swift b/PreviewTests/Sources/GeneratedPreviewTests.swift index 84726015bd..c68538da00 100644 --- a/PreviewTests/Sources/GeneratedPreviewTests.swift +++ b/PreviewTests/Sources/GeneratedPreviewTests.swift @@ -245,6 +245,12 @@ extension PreviewTests { } } + func test_homeScreenKnockedCell() { + for preview in HomeScreenKnockedCell_Previews._allPreviews { + assertSnapshots(matching: preview) + } + } + func test_homeScreenRecoveryKeyConfirmationBanner() { for preview in HomeScreenRecoveryKeyConfirmationBanner_Previews._allPreviews { assertSnapshots(matching: preview) diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenKnockedCell-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenKnockedCell-iPad-en-GB.1.png new file mode 100644 index 0000000000..874d536301 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenKnockedCell-iPad-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:37d4585f90bb64bf739101413ed1a7e24db10161670bd0ba38409e146b31645f +size 175190 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenKnockedCell-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenKnockedCell-iPad-pseudo.1.png new file mode 100644 index 0000000000..750e5a441f --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenKnockedCell-iPad-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:216f3373784519707d3659d6282a1c59202f189407c2f1226bdff510a5f9261c +size 188573 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenKnockedCell-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenKnockedCell-iPhone-16-en-GB.1.png new file mode 100644 index 0000000000..b819d05cd5 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenKnockedCell-iPhone-16-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c674069ca803e934769bb070a528e415ca12de00f722fbb67bf0624a1f24305f +size 120759 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenKnockedCell-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenKnockedCell-iPhone-16-pseudo.1.png new file mode 100644 index 0000000000..adc229bec1 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenKnockedCell-iPhone-16-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d39cc8560c7f57e27764b782dd5ef07e31d883d265f675d8e4f394dccd82e69 +size 142235 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Invite.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Invite.png index c20572b221..f190faed8f 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Invite.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Invite.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:227bd893c17ef8a6042e6eab9372eb78c3cbb631c4b232f13d6e6f0179ce02c7 -size 1982377 +oid sha256:d5b3a8bd64746aad774b15fc3e3500bbab4144d64ed4f8714f9b2527c70a494c +size 1990774 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Join.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Join.png index 7fc18921f5..a140b1c4b7 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Join.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Join.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:146c20f80acac9bf9497aa93562cb930bea02e6fb82507d971a22c26bf3e28a6 -size 1946311 +oid sha256:b6be25a2a046c3d1088ae6f87e4e1e0033062225c624095d679f1083ffe538b6 +size 1945746 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knock.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knock.png index 3b61214cdc..80328b815e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knock.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knock.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:adb3e3a08681e2febea9cd377862eededc6b576bfe06c753bf075eb886f55f9f -size 1788568 +oid sha256:46447c25230d95f04d122924d938f6f4b5eacd633140e586c3c276322f8e8b75 +size 1791354 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knocked.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knocked.png index 36123d267e..be7b7aed9e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knocked.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Knocked.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:47bda34db0009a5f2d04a9ab3fdb761d9efb266046c85abf786051ec3d30f081 -size 1973723 +oid sha256:e0070dcdfe989211600c14fdb7b96af79cfb2b50ae8c3d560f7fe738b368be48 +size 1982637 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Unknown.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Unknown.png index f57487803d..0b5feb40a9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Unknown.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-en-GB.Unknown.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:efda9145642c0bef47507f9de249565cb09897bfed8f4fa6ae79ec792c067fb5 -size 1890674 +oid sha256:c85ef3dcec00111c6c6bb9fe6cfbb1f2bbf332631cc681ea41642f411a4c761e +size 1890475 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Invite.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Invite.png index 5bc940cd75..3691161625 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Invite.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Invite.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:91e9b65c2ad0972f12351bad8d93c627a9d73a87cb22356cd9fc53c81625ed7d -size 1983445 +oid sha256:41f91af2b20ff571cdf281d4dedaf1f6749a8fdaf33508a1b70511ef6e477af4 +size 1995837 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Join.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Join.png index d9ded5d4e5..daf9980781 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Join.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Join.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:48852e2e65d9c3000b2069521628f577426801ab4a804a1f30100540a573bc66 -size 1947292 +oid sha256:daf1b08dbe20c63aa66d1c16e3bc41fdc92a54ad4f143ccfcd9ca8382964a0fe +size 1946727 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knock.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knock.png index d34ffa65c4..e0acdc11eb 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knock.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knock.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:338b8be69ace718a2796101e2ccfbe93dca412e299279a6078b785fec10e7e4b -size 1797161 +oid sha256:08e0296e33a657e0c42f95cb2e4963cb8f32c3a37979262cd76bc4c3ac3a7132 +size 1799819 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knocked.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knocked.png index c32281cae2..1a6c7a7702 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knocked.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Knocked.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d768cff2a5e42c223808f4fc3b1b3f7fbfe13d94711ae2ddeb77d24c2088517 -size 1990759 +oid sha256:8283d5d60db3b91caa096216e4dbb1d5e5342375080c81e5814acbb01b41c8c9 +size 2000079 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Unknown.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Unknown.png index 9b611d6ef7..e3b1a370d0 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Unknown.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPad-pseudo.Unknown.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b1e33030ab7373ae71f3ac6b2580a1187f8ec21d81da94831a3f8ea6fdbcfc0a -size 1895360 +oid sha256:0fde3ae365ead45c6789493ec6dcd5569f420d823d778ebe2549b36e1076052a +size 1895270 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Invite.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Invite.png index c971358184..30981c1a0a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Invite.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Invite.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5bb2adb3c27c6adad8d72174d662688ce1b40a90d748b00354717abf5d37f56 -size 817765 +oid sha256:616a8a5fe92ef87797d9a69bd5d7581e685edad21b002bf079d1a02fdb782987 +size 827638 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Join.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Join.png index c1384ca8df..4e9483a8df 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Join.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Join.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eca394ab7064165321d8eba0c1110b56c5e815260cef52f69cc61ec7c4bc7d3b -size 796819 +oid sha256:6d7390ef83a9fb7e8887aff95c03282d4fbcf3826cf3e254d523373a2c434df0 +size 798961 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knock.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knock.png index f0f6bd47a8..ce2bdf2f6b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knock.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knock.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0be47f8b5c89c0d03e4713e32686c457452851ccc246bf4588f5cb5f82fdbabc -size 701611 +oid sha256:3f9518a386e4c12c9e1527eb5f841fb0ca56fc8658529b58d6d459376caca39b +size 705027 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knocked.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knocked.png index 7f840a1715..8db4a97964 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knocked.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Knocked.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f21b9720b09a4fdc89f4f1f25548200d159b404ed6431693defe99c401f99d2 -size 812339 +oid sha256:19d3b48e373ed4e263c9cb0f6e5a3566a9c6612f19da8468be420eca0a10cd92 +size 819795 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Unknown.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Unknown.png index 90e6e10896..c9a6df3a90 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Unknown.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-en-GB.Unknown.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c001018d811bda7d9db87503f38d94dc23a2102bc134d08b69e3af171ecbf350 -size 755374 +oid sha256:068da1ad317aefc4ebed25f4ac259cd5fe46a5ce444b6e22051455d69bfea78e +size 756893 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Invite.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Invite.png index c70e647ccd..1de1b350e5 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Invite.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Invite.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a922e59c0fec9924f16c3e224af836de79d818fab3e686c1fb34c707be1fc23 -size 820225 +oid sha256:6ecca9bbfa7a1d245a96fc27aacfb5d60931ba1437639ebd8e9ff1756bd705c6 +size 835366 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Join.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Join.png index 0acd1dcc66..42fb8407bb 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Join.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Join.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fb7955f605d2ad9e5678438af15a1a354baab5f00e59ca0ff458759e93d76b1 -size 798533 +oid sha256:450d28a2af4b81f2db2e8a1fb28405d029439cabf5f48688b448391dd7e43b05 +size 800675 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knock.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knock.png index 999c04fe9b..dfd17d6251 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knock.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knock.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:471ae5bf707a3f9e91b7f8cea098287bb21d618297d027a72c105575b5049ff8 -size 709284 +oid sha256:858759589a323ce04e894f398c230c0454f64a3c3148b183f9ecdbeb29955b89 +size 714177 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knocked.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knocked.png index 81a9e865f9..8407998f15 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knocked.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Knocked.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ae617786ed8ee75c55bf33222bdf80faca1dac30a5d0fca84175e3ed7f42d9f -size 829745 +oid sha256:eb3a1c2ac88a74a4f9c402a46a8512abc2596937b92c3d17fa30a7cdf10ce22a +size 835796 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Unknown.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Unknown.png index e943319e83..31f5cf6ebb 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Unknown.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_joinRoomScreen-iPhone-16-pseudo.Unknown.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:93d65076d29abb7a6476b649fae781a8be6bad9d4b905c8251b7253498af3d3e -size 761944 +oid sha256:eaa7027ce0d6ba02e75bc6b35cd7080a4cf217e6d36b3aa2bcebfce6355ad117 +size 763528 diff --git a/UnitTests/Sources/HomeScreenRoomTests.swift b/UnitTests/Sources/HomeScreenRoomTests.swift index bb47ffc0ce..1cbe828d23 100644 --- a/UnitTests/Sources/HomeScreenRoomTests.swift +++ b/UnitTests/Sources/HomeScreenRoomTests.swift @@ -23,8 +23,7 @@ class HomeScreenRoomTests: XCTestCase { hasOngoingCall: Bool) { roomSummary = RoomSummary(roomListItem: .init(noPointer: .init()), id: "Test room", - isInvite: false, - inviter: nil, + joinRequestType: nil, name: "Test room", isDirect: false, avatarURL: nil, diff --git a/UnitTests/Sources/JoinRoomScreenViewModelTests.swift b/UnitTests/Sources/JoinRoomScreenViewModelTests.swift index 42b1b6a526..2f8b6e0521 100644 --- a/UnitTests/Sources/JoinRoomScreenViewModelTests.swift +++ b/UnitTests/Sources/JoinRoomScreenViewModelTests.swift @@ -56,6 +56,23 @@ class JoinRoomScreenViewModelTests: XCTestCase { }.fulfill() } + func testCancelKnock() async throws { + setupViewModel(knocked: true) + + try await deferFulfillment(viewModel.context.$viewState) { state in + state.mode == .knocked + }.fulfill() + + context.send(viewAction: .cancelKnock) + XCTAssertEqual(viewModel.context.alertInfo?.id, .cancelKnock) + + let deferred = deferFulfillment(viewModel.actionsPublisher) { action in + action == .dismiss + } + context.alertInfo?.secondaryButton?.action?() + try await deferred.fulfill() + } + private func setupViewModel(throwing: Bool = false, knocked: Bool = false) { let clientProxy = ClientProxyMock(.init()) @@ -75,7 +92,10 @@ class JoinRoomScreenViewModelTests: XCTestCase { if knocked { clientProxy.roomForIdentifierClosure = { _ in - .knocked(KnockedRoomProxyMock(.init())) + let roomProxy = KnockedRoomProxyMock(.init()) + // to test the cancel knock function + roomProxy.cancelKnockUnderlyingReturnValue = .success(()) + return .knocked(roomProxy) } } diff --git a/UnitTests/Sources/LoggingTests.swift b/UnitTests/Sources/LoggingTests.swift index 20205e5209..3ab0e77fdd 100644 --- a/UnitTests/Sources/LoggingTests.swift +++ b/UnitTests/Sources/LoggingTests.swift @@ -80,8 +80,7 @@ class LoggingTests: XCTestCase { let heroName = "Pseudonym" let roomSummary = RoomSummary(roomListItem: .init(noPointer: .init()), id: "myroomid", - isInvite: false, - inviter: nil, + joinRequestType: nil, name: roomName, isDirect: true, avatarURL: nil, diff --git a/UnitTests/Sources/RoomSummaryTests.swift b/UnitTests/Sources/RoomSummaryTests.swift index 55cb93948d..2626351eb4 100644 --- a/UnitTests/Sources/RoomSummaryTests.swift +++ b/UnitTests/Sources/RoomSummaryTests.swift @@ -56,8 +56,7 @@ class RoomSummaryTests: XCTestCase { func makeSummary(isDirect: Bool, hasRoomAvatar: Bool) -> RoomSummary { RoomSummary(roomListItem: .init(noPointer: .init()), id: roomDetails.id, - isInvite: false, - inviter: nil, + joinRequestType: nil, name: roomDetails.name, isDirect: isDirect, avatarURL: hasRoomAvatar ? roomDetails.avatarURL : nil, From 35cbc84a99f3a7d78877504ff64267c7cfa459c7 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Fri, 25 Oct 2024 10:13:11 +0200 Subject: [PATCH 081/114] min macos support --- ElementX.xcodeproj/project.pbxproj | 2 ++ project.yml | 1 + 2 files changed, 3 insertions(+) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 1d2e485bd4..5f5493965f 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7468,6 +7468,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.6; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; + MACOSX_DEPLOYMENT_TARGET = 14.6; MARKETING_VERSION = 1.9.4; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -7544,6 +7545,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.6; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; + MACOSX_DEPLOYMENT_TARGET = 14.6; MARKETING_VERSION = 1.9.4; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; diff --git a/project.yml b/project.yml index 51f71ea1c2..c0858dd2ab 100644 --- a/project.yml +++ b/project.yml @@ -11,6 +11,7 @@ options: createIntermediateGroups: true deploymentTarget: iOS: '17.6' + macOS: '14.6' groupOrdering: - order: - ElementX From 7a47e37d38ef312db5b66b04dc7c365708148b51 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Fri, 25 Oct 2024 19:58:56 +0300 Subject: [PATCH 082/114] Fix #1934 - Hook reaction pickers into the system's recently used keyboard emojis (#3453) --- ElementX.xcodeproj/project.pbxproj | 12 ++-- .../en.lproj/Localizable.strings | 28 +++++--- .../Sources/Application/AppSettings.swift | 4 ++ .../PinnedEventsTimelineFlowCoordinator.swift | 8 ++- .../RoomFlowCoordinator.swift | 3 +- .../UserSessionFlowCoordinator.swift | 2 +- ElementX/Sources/Generated/Strings.swift | 37 +++++++--- .../EmojiPickerScreenModels.swift | 2 + .../EmojiPickerScreenViewModel.swift | 1 + .../View/EmojiPickerScreen.swift | 4 +- ...innedEventsTimelineScreenCoordinator.swift | 4 +- .../View/PinnedEventsTimelineScreen.swift | 6 +- .../RoomScreen/RoomScreenCoordinator.swift | 3 +- .../Screens/RoomScreen/View/RoomScreen.swift | 6 +- .../DeveloperOptionsScreenModels.swift | 1 + .../View/DeveloperOptionsScreen.swift | 4 ++ .../Screens/Timeline/TimelineModels.swift | 2 + .../Screens/Timeline/TimelineViewModel.swift | 16 +++-- .../View/ItemMenu/TimelineItemMenu.swift | 3 +- .../ItemMenu/TimelineItemMenuAction.swift | 28 +++++--- .../TimelineItemMenuActionProvider.swift | 12 +++- .../ReadReceiptsSummaryView.swift | 3 +- .../Style/TimelineItemBubbledStylerView.swift | 6 +- .../TimelineReadReceiptsView.swift | 3 +- .../HighlightedTimelineItemModifier.swift | 3 +- .../Screens/Timeline/View/TimelineView.swift | 3 +- .../Services/Emojis/EmojiCategory.swift | 13 ---- .../Sources/Services/Emojis/EmojiItem.swift | 19 ----- .../Services/Emojis/EmojiProvider.swift | 69 +++++++++++++++---- .../Emojis/EmojiProviderProtocol.swift | 42 +++++++++++ .../UITests/UITestsAppCoordinator.swift | 26 +++---- UnitTests/Sources/EmojiProviderTests.swift | 8 +-- UnitTests/Sources/PillContextTests.swift | 9 ++- .../Sources/RoomFlowCoordinatorTests.swift | 2 +- .../Sources/TimelineViewModelTests.swift | 15 ++-- 35 files changed, 274 insertions(+), 133 deletions(-) delete mode 100644 ElementX/Sources/Services/Emojis/EmojiCategory.swift delete mode 100644 ElementX/Sources/Services/Emojis/EmojiItem.swift create mode 100644 ElementX/Sources/Services/Emojis/EmojiProviderProtocol.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 5f5493965f..13a12b0268 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -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 = ""; }; 36FD673E24FBFCFDF398716A /* RoomMemberProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberProxyMock.swift; sourceTree = ""; }; 376D941BF8BB294389C0DE24 /* MapTilerURLBuildersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerURLBuildersTests.swift; sourceTree = ""; }; - 37A243E04B58DC6E41FDCD82 /* EmojiItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiItem.swift; sourceTree = ""; }; 37A63A59BFDDC494B1C20119 /* CallScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallScreenViewModel.swift; sourceTree = ""; }; 37CA26F55123E36B50DB0B3A /* AttributedStringTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringTests.swift; sourceTree = ""; }; 37FEE10AB666891E6A675E5E /* SecureBackupLogoutConfirmationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreen.swift; sourceTree = ""; }; @@ -1474,7 +1472,6 @@ 3BAC027034248429A438886B /* AppMediatorMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediatorMock.swift; sourceTree = ""; }; 3BC1B7CB061C9865B2B91B56 /* QRCodeLoginScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeLoginScreenViewModel.swift; sourceTree = ""; }; 3BDCCD2F6B405C14B9BCE94E /* JoinRoomScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenCoordinator.swift; sourceTree = ""; }; - 3C1A3D524D63815B28FA4D62 /* EmojiCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCategory.swift; sourceTree = ""; }; 3C368CAB3063EF275357ECD4 /* LoginScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenViewModel.swift; sourceTree = ""; }; 3C3E67E09FE5A35D73818C39 /* AppLockScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenModels.swift; sourceTree = ""; }; 3CCD41CD67DB5DA0D436BFE9 /* VoiceMessageRoomPlaybackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomPlaybackView.swift; sourceTree = ""; }; @@ -1815,6 +1812,7 @@ 8AE0C9653870803E4F91F474 /* RoomListFiltersStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomListFiltersStateTests.swift; sourceTree = ""; }; 8AE78FA0011E07920AE83135 /* PlainMentionBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlainMentionBuilder.swift; sourceTree = ""; }; 8AFCE895ECFFA53FEE64D62B /* MediaLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaLoader.swift; sourceTree = ""; }; + 8BCCE3D12B0A9C6E559B5B5A /* EmojiProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiProviderProtocol.swift; sourceTree = ""; }; 8BEBF0E59F25E842EDB6FD11 /* LocationSharingScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSharingScreenModels.swift; sourceTree = ""; }; 8C44BBC892499BE45B074F89 /* AppLockScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenCoordinator.swift; sourceTree = ""; }; 8C8616254EE40CA8BA5E9BC2 /* VideoRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineItemContent.swift; sourceTree = ""; }; @@ -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 = ""; @@ -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 */, diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 4aeb3bef7b..9a1b11ba00 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -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…"; diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index fe6d11d335..7ec77ce53b 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -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 diff --git a/ElementX/Sources/FlowCoordinators/PinnedEventsTimelineFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/PinnedEventsTimelineFlowCoordinator.swift index fd021d3cbb..f900329307 100644 --- a/ElementX/Sources/FlowCoordinators/PinnedEventsTimelineFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/PinnedEventsTimelineFlowCoordinator.swift @@ -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 = .init() var actionsPublisher: AnyPublisher { @@ -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 diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index 5eabe89580..b5af3ad085 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -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 { diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift index e99fc21f61..c5b64b2208 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift @@ -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, diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index a9fef6619b..04e3312aad 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -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") } diff --git a/ElementX/Sources/Screens/EmojiPickerScreen/EmojiPickerScreenModels.swift b/ElementX/Sources/Screens/EmojiPickerScreen/EmojiPickerScreenModels.swift index 5aa2c4cd95..a56940a6dd 100644 --- a/ElementX/Sources/Screens/EmojiPickerScreen/EmojiPickerScreenModels.swift +++ b/ElementX/Sources/Screens/EmojiPickerScreen/EmojiPickerScreenModels.swift @@ -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 "" diff --git a/ElementX/Sources/Screens/EmojiPickerScreen/EmojiPickerScreenViewModel.swift b/ElementX/Sources/Screens/EmojiPickerScreen/EmojiPickerScreenViewModel.swift index ce71384ca6..a50238258f 100644 --- a/ElementX/Sources/Screens/EmojiPickerScreen/EmojiPickerScreenViewModel.swift +++ b/ElementX/Sources/Screens/EmojiPickerScreen/EmojiPickerScreenViewModel.swift @@ -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) diff --git a/ElementX/Sources/Screens/EmojiPickerScreen/View/EmojiPickerScreen.swift b/ElementX/Sources/Screens/EmojiPickerScreen/View/EmojiPickerScreen.swift index 9f5e39325f..7f139f79a1 100644 --- a/ElementX/Sources/Screens/EmojiPickerScreen/View/EmojiPickerScreen.swift +++ b/ElementX/Sources/Screens/EmojiPickerScreen/View/EmojiPickerScreen.swift @@ -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") diff --git a/ElementX/Sources/Screens/PinnedEventsTimelineScreen/PinnedEventsTimelineScreenCoordinator.swift b/ElementX/Sources/Screens/PinnedEventsTimelineScreen/PinnedEventsTimelineScreenCoordinator.swift index 850f7026eb..3c7cf46e0f 100644 --- a/ElementX/Sources/Screens/PinnedEventsTimelineScreen/PinnedEventsTimelineScreenCoordinator.swift +++ b/ElementX/Sources/Screens/PinnedEventsTimelineScreen/PinnedEventsTimelineScreenCoordinator.swift @@ -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() { diff --git a/ElementX/Sources/Screens/PinnedEventsTimelineScreen/View/PinnedEventsTimelineScreen.swift b/ElementX/Sources/Screens/PinnedEventsTimelineScreen/View/PinnedEventsTimelineScreen.swift index c144ae4666..8a18ec2744 100644 --- a/ElementX/Sources/Screens/PinnedEventsTimelineScreen/View/PinnedEventsTimelineScreen.swift +++ b/ElementX/Sources/Screens/PinnedEventsTimelineScreen/View/PinnedEventsTimelineScreen.swift @@ -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 { diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift index 16b4e2f495..1f63793857 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift @@ -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, diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift index 33023ad766..9e187d8ab3 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift @@ -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 { diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift index 1d282d462d..c416a8ec1b 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift @@ -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 { } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift index 22f7044896..5768901b14 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -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") { diff --git a/ElementX/Sources/Screens/Timeline/TimelineModels.swift b/ElementX/Sources/Screens/Timeline/TimelineModels.swift index 44a7ed0834..363ddb9fa1 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineModels.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineModels.swift @@ -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 { diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index 7d35cb7a73..56307073d6 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -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 { diff --git a/ElementX/Sources/Screens/Timeline/View/ItemMenu/TimelineItemMenu.swift b/ElementX/Sources/Screens/Timeline/View/ItemMenu/TimelineItemMenu.swift index 527e54f3d8..86a5ccead6 100644 --- a/ElementX/Sources/Screens/Timeline/View/ItemMenu/TimelineItemMenu.swift +++ b/ElementX/Sources/Screens/Timeline/View/ItemMenu/TimelineItemMenu.swift @@ -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 } diff --git a/ElementX/Sources/Screens/Timeline/View/ItemMenu/TimelineItemMenuAction.swift b/ElementX/Sources/Screens/Timeline/View/ItemMenu/TimelineItemMenuAction.swift index 0ef3a10d35..a2e7de4f20 100644 --- a/ElementX/Sources/Screens/Timeline/View/ItemMenu/TimelineItemMenuAction.swift +++ b/ElementX/Sources/Screens/Timeline/View/ItemMenu/TimelineItemMenuAction.swift @@ -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 { [] } diff --git a/ElementX/Sources/Screens/Timeline/View/ItemMenu/TimelineItemMenuActionProvider.swift b/ElementX/Sources/Screens/Timeline/View/ItemMenu/TimelineItemMenuActionProvider.swift index f3b79fb5df..9fcc47575a 100644 --- a/ElementX/Sources/Screens/Timeline/View/ItemMenu/TimelineItemMenuActionProvider.swift +++ b/ElementX/Sources/Screens/Timeline/View/ItemMenu/TimelineItemMenuActionProvider.swift @@ -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 { diff --git a/ElementX/Sources/Screens/Timeline/View/ReadReceipts/ReadReceiptsSummaryView.swift b/ElementX/Sources/Screens/Timeline/View/ReadReceipts/ReadReceiptsSummaryView.swift index 414266fa45..2de58b9edd 100644 --- a/ElementX/Sources/Screens/Timeline/View/ReadReceipts/ReadReceiptsSummaryView.swift +++ b/ElementX/Sources/Screens/Timeline/View/ReadReceipts/ReadReceiptsSummaryView.swift @@ -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 }() diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift index 049a788311..61ed78ee82 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift @@ -148,7 +148,8 @@ struct TimelineItemBubbledStylerView: 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 { diff --git a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift index f3f88fedd1..6f37e7f56e 100644 --- a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift @@ -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"), diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/HighlightedTimelineItemModifier.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/HighlightedTimelineItemModifier.swift index efb0566cd1..cb5695bdff 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/HighlightedTimelineItemModifier.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/HighlightedTimelineItemModifier.swift @@ -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 { diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineView.swift index c9133b242e..5315b57884 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineView.swift @@ -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 { diff --git a/ElementX/Sources/Services/Emojis/EmojiCategory.swift b/ElementX/Sources/Services/Emojis/EmojiCategory.swift deleted file mode 100644 index 7e42a1f819..0000000000 --- a/ElementX/Sources/Services/Emojis/EmojiCategory.swift +++ /dev/null @@ -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] -} diff --git a/ElementX/Sources/Services/Emojis/EmojiItem.swift b/ElementX/Sources/Services/Emojis/EmojiItem.swift deleted file mode 100644 index d08cbb0abf..0000000000 --- a/ElementX/Sources/Services/Emojis/EmojiItem.swift +++ /dev/null @@ -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] -} diff --git a/ElementX/Sources/Services/Emojis/EmojiProvider.swift b/ElementX/Sources/Services/Emojis/EmojiProvider.swift index 5272902183..dfa0cb70a2 100644 --- a/ElementX/Sources/Services/Emojis/EmojiProvider.swift +++ b/ElementX/Sources/Services/Emojis/EmojiProvider.swift @@ -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 diff --git a/ElementX/Sources/Services/Emojis/EmojiProviderProtocol.swift b/ElementX/Sources/Services/Emojis/EmojiProviderProtocol.swift new file mode 100644 index 0000000000..b0137716e8 --- /dev/null +++ b/ElementX/Sources/Services/Emojis/EmojiProviderProtocol.swift @@ -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) +} diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index fb12887859..1d4e6ca035 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -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, diff --git a/UnitTests/Sources/EmojiProviderTests.swift b/UnitTests/Sources/EmojiProviderTests.swift index 4733122b8c..799a67ee2a 100644 --- a/UnitTests/Sources/EmojiProviderTests.swift +++ b/UnitTests/Sources/EmojiProviderTests.swift @@ -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) diff --git a/UnitTests/Sources/PillContextTests.swift b/UnitTests/Sources/PillContextTests.swift index 03b1b462e0..7f7f72e556 100644 --- a/UnitTests/Sources/PillContextTests.swift +++ b/UnitTests/Sources/PillContextTests.swift @@ -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) diff --git a/UnitTests/Sources/RoomFlowCoordinatorTests.swift b/UnitTests/Sources/RoomFlowCoordinatorTests.swift index a10fa0f591..18e20f4524 100644 --- a/UnitTests/Sources/RoomFlowCoordinatorTests.swift +++ b/UnitTests/Sources/RoomFlowCoordinatorTests.swift @@ -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, diff --git a/UnitTests/Sources/TimelineViewModelTests.swift b/UnitTests/Sources/TimelineViewModelTests.swift index d0a95e8131..28f2ad0d32 100644 --- a/UnitTests/Sources/TimelineViewModelTests.swift +++ b/UnitTests/Sources/TimelineViewModelTests.swift @@ -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)) } } From b514d54b3dc9f67f35d9368b5dc13435986466af Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Sat, 26 Oct 2024 18:23:42 +0300 Subject: [PATCH 083/114] Fix the order of the frequently used emojis when showing them in the full reaction picker. --- .../Sources/Services/Emojis/EmojiProvider.swift | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/ElementX/Sources/Services/Emojis/EmojiProvider.swift b/ElementX/Sources/Services/Emojis/EmojiProvider.swift index dfa0cb70a2..80c31ed0d5 100644 --- a/ElementX/Sources/Services/Emojis/EmojiProvider.swift +++ b/ElementX/Sources/Services/Emojis/EmojiProvider.swift @@ -10,6 +10,7 @@ import Foundation import OrderedCollections class EmojiProvider: EmojiProviderProtocol { + private let maxFrequentEmojis = 20 private let loader: EmojiLoaderProtocol private let appSettings: AppSettings @@ -31,9 +32,18 @@ class EmojiProvider: EmojiProviderProtocol { 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) } + // Map frequently used system unicode emojis to our emoji provider ones and preserve the order + let frequentlyUsedEmojis = frequentlyUsedSystemEmojis().prefix(maxFrequentEmojis) + let emojis = allEmojis + .filter { frequentlyUsedEmojis.contains($0.unicode) } + .sorted { first, second in + guard let firstIndex = frequentlyUsedEmojis.firstIndex(of: first.unicode), + let secondIndex = frequentlyUsedEmojis.firstIndex(of: second.unicode) else { + return false + } + + return firstIndex < secondIndex + } if !emojis.isEmpty { emojiCategories.insert(.init(id: EmojiCategory.frequentlyUsedCategoryIdentifier, emojis: emojis), at: 0) From ed8aed65c7a4df5b22106891034d2be04322a3e1 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 28 Oct 2024 12:12:30 +0000 Subject: [PATCH 084/114] Revert "min macos support" (#3458) This reverts commit 35cbc84a99f3a7d78877504ff64267c7cfa459c7. --- ElementX.xcodeproj/project.pbxproj | 2 -- project.yml | 1 - 2 files changed, 3 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 13a12b0268..d762d3b043 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7464,7 +7464,6 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.6; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; - MACOSX_DEPLOYMENT_TARGET = 14.6; MARKETING_VERSION = 1.9.4; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -7541,7 +7540,6 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.6; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; - MACOSX_DEPLOYMENT_TARGET = 14.6; MARKETING_VERSION = 1.9.4; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; diff --git a/project.yml b/project.yml index c0858dd2ab..51f71ea1c2 100644 --- a/project.yml +++ b/project.yml @@ -11,7 +11,6 @@ options: createIntermediateGroups: true deploymentTarget: iOS: '17.6' - macOS: '14.6' groupOrdering: - order: - ElementX From 4e812f72b904d76983e27e4829ed6e43f0f00857 Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Mon, 28 Oct 2024 12:16:38 +0000 Subject: [PATCH 085/114] Enable identity pinning violation notifications unconditionally (#3457) (Remove the feature flag we added when this feature seemed unstable.) --- ElementX/Sources/Application/AppSettings.swift | 4 ---- ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift | 4 +--- .../DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift | 1 - .../DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift | 4 ---- 4 files changed, 1 insertion(+), 12 deletions(-) diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index 7ec77ce53b..4762449230 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -46,7 +46,6 @@ final class AppSettings { case publicSearchEnabled case fuzzyRoomListSearchEnabled case enableOnlySignedDeviceIsolationMode - case identityPinningViolationNotificationsEnabled case knockingEnabled case frequentEmojisEnabled } @@ -284,9 +283,6 @@ final class AppSettings { @UserPreference(key: UserDefaultsKeys.optimizeMediaUploads, defaultValue: false, storageType: .userDefaults(store)) var optimizeMediaUploads - - @UserPreference(key: UserDefaultsKeys.identityPinningViolationNotificationsEnabled, defaultValue: isDevelopmentBuild, storageType: .userDefaults(store)) - var identityPinningViolationNotificationsEnabled @UserPreference(key: UserDefaultsKeys.knockingEnabled, defaultValue: false, storageType: .userDefaults(store)) var knockingEnabled diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index 956ae8210a..36f3bb7239 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -142,9 +142,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } .store(in: &cancellables) - let identityStatusChangesPublisher = roomProxy.identityStatusChangesPublisher - .receive(on: DispatchQueue.main) - .filter { [weak self] _ in self?.appSettings.identityPinningViolationNotificationsEnabled ?? false } + let identityStatusChangesPublisher = roomProxy.identityStatusChangesPublisher.receive(on: DispatchQueue.main) Task { [weak self] in for await changes in identityStatusChangesPublisher.values { diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift index c416a8ec1b..62e4d7f9b0 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift @@ -49,7 +49,6 @@ protocol DeveloperOptionsProtocol: AnyObject { var optimizeMediaUploads: Bool { get set } var enableOnlySignedDeviceIsolationMode: Bool { get set } var elementCallBaseURLOverride: URL? { get set } - var identityPinningViolationNotificationsEnabled: Bool { get set } var knockingEnabled: Bool { get set } var frequentEmojisEnabled: Bool { get set } } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift index 5768901b14..bf4ed095aa 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -50,10 +50,6 @@ struct DeveloperOptionsScreen: View { Text("Hide image & video previews") } - Toggle(isOn: $context.identityPinningViolationNotificationsEnabled) { - Text("Identity pinning violation notifications") - } - Toggle(isOn: $context.frequentEmojisEnabled) { Text("Show frequently used emojis") } From 7c28d9709fcaaf5cccb6038ce924f476762eb18b Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 28 Oct 2024 12:28:13 +0000 Subject: [PATCH 086/114] Tweak the flow for changing a recovery key. (#3452) * Rename Chat Backup setting to Encryption. * Update Key Storage strings on SecureBackupScreen. * Update strings/design on SecureBackupRecoveryKeyScreen. --- .../SecureBackupRecoveryKeyScreenModels.swift | 1 + ...cureBackupRecoveryKeyScreenViewModel.swift | 4 +- .../View/SecureBackupRecoveryKeyScreen.swift | 81 +++++++++++++------ .../SecureBackupScreenModels.swift | 5 +- .../SecureBackupScreenViewModel.swift | 29 +++++-- .../View/SecureBackupScreen.swift | 29 +++---- .../SettingsScreen/View/SettingsScreen.swift | 2 +- ...ecoveryKeyScreen-iPad-en-GB.Generating.png | 3 + ...ecoveryKeyScreen-iPad-en-GB.Incomplete.png | 4 +- ...ecoveryKeyScreen-iPad-en-GB.Not-set-up.png | 4 +- ...kupRecoveryKeyScreen-iPad-en-GB.Set-up.png | 4 +- ...upRecoveryKeyScreen-iPad-en-GB.Unknown.png | 4 +- ...coveryKeyScreen-iPad-pseudo.Generating.png | 3 + ...coveryKeyScreen-iPad-pseudo.Incomplete.png | 4 +- ...coveryKeyScreen-iPad-pseudo.Not-set-up.png | 4 +- ...upRecoveryKeyScreen-iPad-pseudo.Set-up.png | 4 +- ...pRecoveryKeyScreen-iPad-pseudo.Unknown.png | 4 +- ...ryKeyScreen-iPhone-16-en-GB.Generating.png | 3 + ...ryKeyScreen-iPhone-16-en-GB.Incomplete.png | 4 +- ...ryKeyScreen-iPhone-16-en-GB.Not-set-up.png | 4 +- ...coveryKeyScreen-iPhone-16-en-GB.Set-up.png | 4 +- ...overyKeyScreen-iPhone-16-en-GB.Unknown.png | 4 +- ...yKeyScreen-iPhone-16-pseudo.Generating.png | 3 + ...yKeyScreen-iPhone-16-pseudo.Incomplete.png | 4 +- ...yKeyScreen-iPhone-16-pseudo.Not-set-up.png | 4 +- ...overyKeyScreen-iPhone-16-pseudo.Set-up.png | 4 +- ...veryKeyScreen-iPhone-16-pseudo.Unknown.png | 4 +- ...cureBackupScreen-iPad-en-GB.Both-setup.png | 4 +- ...pScreen-iPad-en-GB.Key-backup-disabled.png | 4 +- ...creen-iPad-en-GB.Only-key-backup-setup.png | 4 +- ...ureBackupScreen-iPad-pseudo.Both-setup.png | 4 +- ...Screen-iPad-pseudo.Key-backup-disabled.png | 4 +- ...reen-iPad-pseudo.Only-key-backup-setup.png | 4 +- ...ackupScreen-iPhone-16-en-GB.Both-setup.png | 4 +- ...en-iPhone-16-en-GB.Key-backup-disabled.png | 4 +- ...-iPhone-16-en-GB.Only-key-backup-setup.png | 4 +- ...ckupScreen-iPhone-16-pseudo.Both-setup.png | 4 +- ...n-iPhone-16-pseudo.Key-backup-disabled.png | 4 +- ...iPhone-16-pseudo.Only-key-backup-setup.png | 4 +- .../test_settingsScreen-iPad-en-GB.1.png | 4 +- .../test_settingsScreen-iPad-pseudo.1.png | 4 +- .../test_settingsScreen-iPhone-16-en-GB.1.png | 4 +- ...test_settingsScreen-iPhone-16-pseudo.1.png | 4 +- 43 files changed, 175 insertions(+), 116 deletions(-) create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Generating.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Generating.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Generating.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Generating.png diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenModels.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenModels.swift index b39de38910..f04790123c 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenModels.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenModels.swift @@ -27,6 +27,7 @@ struct SecureBackupRecoveryKeyScreenViewState: BindableState { let mode: SecureBackupRecoveryKeyScreenViewMode var recoveryKey: String? + var isGeneratingKey = false var doneButtonEnabled = false var bindings: SecureBackupRecoveryKeyScreenViewBindings diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift index 35a51a48d1..217b27029e 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift @@ -49,6 +49,8 @@ class SecureBackupRecoveryKeyScreenViewModel: SecureBackupRecoveryKeyScreenViewM switch viewAction { case .generateKey: + state.isGeneratingKey = true + Task { switch await secureBackupController.generateRecoveryKey() { case .success(let key): @@ -58,7 +60,7 @@ class SecureBackupRecoveryKeyScreenViewModel: SecureBackupRecoveryKeyScreenViewM state.bindings.alertInfo = .init(id: .init()) } - hideLoadingIndicator() + state.isGeneratingKey = false } case .copyKey: UIPasteboard.general.string = state.recoveryKey diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift index 995963a445..db36934f90 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift @@ -18,7 +18,6 @@ struct SecureBackupRecoveryKeyScreen: View { FullscreenDialog { ScrollViewReader { reader in mainContent - .padding(16) .onChange(of: focused) { _, newValue in guard newValue == true else { return } reader.scrollTo(textFieldIdentifier) @@ -94,12 +93,12 @@ struct SecureBackupRecoveryKeyScreen: View { } private var recoveryCreatedActionButtons: some View { - VStack(spacing: 8.0) { + VStack(spacing: 16) { if let recoveryKey = context.viewState.recoveryKey { ShareLink(item: recoveryKey) { Label(L10n.screenRecoveryKeySaveAction, icon: \.download) } - .buttonStyle(.compound(.primary)) + .buttonStyle(.compound(.secondary)) .simultaneousGesture(TapGesture().onEnded { _ in context.send(viewAction: .keySaved) }) @@ -131,20 +130,31 @@ struct SecureBackupRecoveryKeyScreen: View { Text(L10n.commonRecoveryKey) .foregroundColor(.compound.textPrimary) .font(.compound.bodySMSemibold) + .padding(.leading, 16) Group { if context.viewState.recoveryKey == nil { - Button(generateButtonTitle) { - context.send(viewAction: .generateKey) + if !context.viewState.isGeneratingKey { + Button(generateButtonTitle) { + context.send(viewAction: .generateKey) + } + .font(.compound.bodyLGSemibold) + .padding(.vertical, 11) + } else { + HStack(spacing: 8) { + ProgressView() + Text(L10n.screenRecoveryKeyGeneratingKey) + } + .font(.compound.bodyLGSemibold) + .foregroundStyle(.compound.textPrimary) + .padding(.vertical, 11) } - .font(.compound.bodyLGSemibold) } else { - HStack(alignment: .top, spacing: 8) { + HStack(spacing: 8) { Text(context.viewState.recoveryKey ?? "") .foregroundColor(.compound.textPrimary) .font(.compound.bodyLG) - - Spacer() + .frame(maxWidth: .infinity, alignment: .leading) Button { context.send(viewAction: .copyKey) @@ -157,21 +167,16 @@ struct SecureBackupRecoveryKeyScreen: View { } } .frame(maxWidth: .infinity) - .padding() + .padding(.vertical, 14) + .padding(.horizontal, 16) .background(Color.compound.bgSubtleSecondaryLevel0) - .clipShape(RoundedRectangle(cornerRadius: 8)) + .clipShape(RoundedRectangle(cornerRadius: 14)) if let subtitle = context.viewState.recoveryKeySubtitle { - Label { - Text(subtitle) - .foregroundColor(.compound.textSecondary) - .font(.compound.bodySM) - } icon: { - if context.viewState.recoveryKey == nil { - CompoundIcon(\.infoSolid, size: .small, relativeTo: .compound.bodySM) - } - } - .labelStyle(.custom(spacing: 8, alignment: .top)) + Text(subtitle) + .foregroundColor(.compound.textSecondary) + .font(.compound.bodySM) + .padding(.leading, 16) } } } @@ -212,8 +217,10 @@ struct SecureBackupRecoveryKeyScreen: View { // MARK: - Previews struct SecureBackupRecoveryKeyScreen_Previews: PreviewProvider, TestablePreview { - static let setupViewModel = viewModel(recoveryState: .enabled) + static let key = "EsTM njec uHYA yHmh dQdW Nj4o bNRU 9jMN XGMc KUNM UFr5 R8GY" static let notSetUpViewModel = viewModel(recoveryState: .disabled) + static let generatingViewModel = viewModel(recoveryState: .disabled, generateKey: true) + static let setupViewModel = viewModel(recoveryState: .enabled, generateKey: true, key: key) static let incompleteViewModel = viewModel(recoveryState: .incomplete) static let unknownViewModel = viewModel(recoveryState: .unknown) @@ -223,10 +230,17 @@ struct SecureBackupRecoveryKeyScreen_Previews: PreviewProvider, TestablePreview } .previewDisplayName("Not set up") + NavigationStack { + SecureBackupRecoveryKeyScreen(context: generatingViewModel.context) + } + .previewDisplayName("Generating") + .snapshot(delay: 0.25) + NavigationStack { SecureBackupRecoveryKeyScreen(context: setupViewModel.context) } .previewDisplayName("Set up") + .snapshot(delay: 0.25) NavigationStack { SecureBackupRecoveryKeyScreen(context: incompleteViewModel.context) @@ -239,12 +253,27 @@ struct SecureBackupRecoveryKeyScreen_Previews: PreviewProvider, TestablePreview .previewDisplayName("Unknown") } - static func viewModel(recoveryState: SecureBackupRecoveryState) -> SecureBackupRecoveryKeyScreenViewModelType { + static func viewModel(recoveryState: SecureBackupRecoveryState, generateKey: Bool = false, key: String? = nil) -> SecureBackupRecoveryKeyScreenViewModelType { let backupController = SecureBackupControllerMock() backupController.underlyingRecoveryState = CurrentValueSubject(recoveryState).asCurrentValuePublisher() - return SecureBackupRecoveryKeyScreenViewModel(secureBackupController: backupController, - userIndicatorController: UserIndicatorControllerMock(), - isModallyPresented: true) + if let key { + backupController.generateRecoveryKeyReturnValue = .success(key) + } else { + backupController.generateRecoveryKeyClosure = { + try? await Task.sleep(for: .seconds(1000)) + return .success("youshouldntseeme") + } + } + + let viewModel = SecureBackupRecoveryKeyScreenViewModel(secureBackupController: backupController, + userIndicatorController: UserIndicatorControllerMock(), + isModallyPresented: true) + + if generateKey { + viewModel.context.send(viewAction: .generateKey) + } + + return viewModel } } diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenModels.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenModels.swift index 1fa713d1c4..34f07c85d4 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenModels.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenModels.swift @@ -16,14 +16,15 @@ struct SecureBackupScreenViewState: BindableState { let chatBackupDetailsURL: URL var recoveryState = SecureBackupRecoveryState.unknown var keyBackupState = SecureBackupKeyBackupState.unknown - var bindings = SecureBackupScreenViewStateBindings() + var bindings: SecureBackupScreenViewStateBindings } struct SecureBackupScreenViewStateBindings { + var keyStorageEnabled: Bool var alertInfo: AlertInfo? } enum SecureBackupScreenViewAction { case recoveryKey - case keyBackup + case keyStorageToggled(Bool) } diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenViewModel.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenViewModel.swift index e3fda71d4d..3f695da2b7 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenViewModel.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenViewModel.swift @@ -25,7 +25,8 @@ class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackup self.secureBackupController = secureBackupController self.userIndicatorController = userIndicatorController - super.init(initialViewState: .init(chatBackupDetailsURL: chatBackupDetailsURL)) + super.init(initialViewState: .init(chatBackupDetailsURL: chatBackupDetailsURL, + bindings: SecureBackupScreenViewStateBindings(keyStorageEnabled: secureBackupController.keyBackupState.value.toggleState))) secureBackupController.recoveryState .receive(on: DispatchQueue.main) @@ -34,7 +35,11 @@ class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackup secureBackupController.keyBackupState .receive(on: DispatchQueue.main) - .weakAssign(to: \.state.keyBackupState, on: self) + .sink { [weak self] state in + guard let self else { return } + self.state.keyBackupState = state + self.state.bindings.keyStorageEnabled = state.toggleState + } .store(in: &cancellables) } @@ -44,11 +49,14 @@ class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackup switch viewAction { case .recoveryKey: actionsSubject.send(.recoveryKey) - case .keyBackup: - switch secureBackupController.keyBackupState.value { - case .unknown: + case .keyStorageToggled(let enable): + let keyBackupState = secureBackupController.keyBackupState.value + switch (keyBackupState, enable) { + case (.unknown, true): + state.bindings.keyStorageEnabled = keyBackupState.toggleState // Reset the toggle in case enabling fails enableBackup() - case .enabled: + case (.enabled, false): + state.bindings.keyStorageEnabled = keyBackupState.toggleState // Reset the toggle in case the user cancels actionsSubject.send(.keyBackup) default: break @@ -74,3 +82,12 @@ class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackup } } } + +private extension SecureBackupKeyBackupState { + var toggleState: Bool { + switch self { + case .unknown, .enabling: false + case .enabled, .disabling: true + } + } +} diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift index cdc0aa2827..040d51e781 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift @@ -39,7 +39,7 @@ struct SecureBackupScreen: View { private var keyBackupSection: some View { Section { ListRow(kind: .custom { - VStack(alignment: .leading, spacing: 2) { + VStack(alignment: .leading, spacing: 8) { Text(L10n.screenChatBackupKeyBackupTitle) .font(.compound.bodyLGSemibold) .foregroundColor(.compound.textPrimary) @@ -53,7 +53,7 @@ struct SecureBackupScreen: View { .accessibilityElement(children: .combine) }) - keyBackupButton + keyStorageToggle } } @@ -67,26 +67,23 @@ struct SecureBackupScreen: View { return description } - @ViewBuilder - private var keyBackupButton: some View { - switch context.viewState.keyBackupState { - case .enabled, .disabling: - ListRow(label: .plain(title: L10n.screenChatBackupKeyBackupActionDisable, role: .destructive), kind: .navigationLink { - context.send(viewAction: .keyBackup) - }) - case .unknown, .enabling: - ListRow(label: .plain(title: L10n.screenChatBackupKeyBackupActionEnable), kind: .navigationLink { - context.send(viewAction: .keyBackup) - }) - } + private var keyStorageToggle: some View { + ListRow(label: .plain(title: L10n.screenChatBackupKeyStorageToggleTitle, + description: L10n.screenChatBackupKeyStorageToggleDescription), + kind: .toggle($context.keyStorageEnabled)) + .onChange(of: context.keyStorageEnabled) { _, newValue in + context.send(viewAction: .keyStorageToggled(newValue)) + } } - @ViewBuilder private var recoveryKeySection: some View { Section { switch context.viewState.recoveryState { case .enabled: - ListRow(label: .plain(title: L10n.screenChatBackupRecoveryActionChange), + ListRow(label: .default(title: L10n.screenChatBackupRecoveryActionChange, + description: L10n.screenChatBackupRecoveryActionChangeDescription, + icon: \.key, + iconAlignment: .top), kind: .navigationLink { context.send(viewAction: .recoveryKey) }) case .disabled: ListRow(label: .plain(title: L10n.screenChatBackupRecoveryActionSetup), diff --git a/ElementX/Sources/Screens/Settings/SettingsScreen/View/SettingsScreen.swift b/ElementX/Sources/Screens/Settings/SettingsScreen/View/SettingsScreen.swift index efe901dbcd..5e8d027eec 100644 --- a/ElementX/Sources/Screens/Settings/SettingsScreen/View/SettingsScreen.swift +++ b/ElementX/Sources/Screens/Settings/SettingsScreen/View/SettingsScreen.swift @@ -90,7 +90,7 @@ struct SettingsScreen: View { switch context.viewState.securitySectionMode { case .secureBackup: - ListRow(label: .default(title: L10n.commonChatBackup, + ListRow(label: .default(title: L10n.commonEncryption, icon: \.key), details: context.viewState.showSecuritySectionBadge ? .icon(securitySectionBadge) : nil, kind: .navigationLink { context.send(viewAction: .secureBackup) }) diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Generating.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Generating.png new file mode 100644 index 0000000000..a4c309c223 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Generating.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42456c30e4c48f49498df5fb78543864ba73475b0519bb845fd8aea2b2711d2e +size 122723 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Incomplete.png index 70243b15fd..717f756e27 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:18b21094daf27f491931edd1113b76f188c99f3558271208195792db24eea2ad -size 105484 +oid sha256:40ae027d4a68aa576e6c09b86612f26a20287b78feee68828df22a9e75e8d4c0 +size 105493 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Not-set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Not-set-up.png index 9b699b01eb..0513d874d1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Not-set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Not-set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13771e53bc8396d5ad1af3119b78c031f0aa64fcc015e72e45c1e2b712846fd2 -size 125802 +oid sha256:d812c0830f2b0b88db0eba82fcbceb9b4c5698e7c55ad0e9e7fc5a9c83e96249 +size 123915 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Set-up.png index 17e146e141..ebbd6a69f3 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:61ec65771fe434a6c34ea47e015ce32f59521c5c86389c0e3989affc155c7c42 -size 124896 +oid sha256:2b4eb7771b9156b75c3812751b1219ba337e4d9b1b465dedd09e7784b1f009e9 +size 120296 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Unknown.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Unknown.png index fa0f39a05a..c7ea4eaaeb 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Unknown.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Unknown.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0bdd1681f7a87bae31a601bcf4a2e0df62730a8532169505b2aabab3ab6e982 -size 78759 +oid sha256:a8b27413a8893ca1b9797bb349f109a78b99a20eda69e385120d68793254f4dd +size 78755 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Generating.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Generating.png new file mode 100644 index 0000000000..3233c200c5 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Generating.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af3a401be4780e063d611293d06988524e07ea45bd4558d51d0f4f0a937ce531 +size 150029 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Incomplete.png index 3078c007d5..1f751b2d94 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1bc60a89d5aede42baa7c36eafafe88f326712713551c4c738cf8d0cf3a7860c -size 121893 +oid sha256:bd327eb2290a77139a8a3f3d76e99baf7f09d724034c5931dda0485ed2455136 +size 120592 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Not-set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Not-set-up.png index 06f510d612..b01b7ec750 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Not-set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Not-set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8756534bad47d59f637ed3ed5cdde23bd88c58748e6f44df2edf8b4d325e3c27 -size 157434 +oid sha256:1500a5f539dc2ec3faae11c0d5a707f4329a15814d686de570baa901e1420fdf +size 151948 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Set-up.png index 30033bcaa1..37f1a320a7 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b4ceabdf84f86616791951043844e9c618b56b074f16adc97d95bf7418a0d3a -size 151871 +oid sha256:e80a71b9eaeb41bd700cf22e98b0290c06e6786cdffea72c0973549b4ba5bd43 +size 141940 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Unknown.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Unknown.png index 7b3e79dabd..b99ab2fbd5 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Unknown.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Unknown.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:625728b535938291ee7433c2e42c356f5984d59ec9c564b7ec114915cc604f30 -size 80957 +oid sha256:f690078660851b4af18ea78341b352c33f2862b378a19534010dbebe767f8b31 +size 80936 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Generating.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Generating.png new file mode 100644 index 0000000000..ed38a1f77d --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Generating.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d1b0c218ffb7143d8da4567a02fa0741a94942449dd00c065b7415978a100e6d +size 76017 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Incomplete.png index 20c8a315f9..1f3f01be4d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:60ea8f1f8c02fc5f820a3b41fa4fe411124fdfb73c2fd51e771c51673becfbfd -size 61760 +oid sha256:e264708e7ac73c422bf9f13ace0e5f590958f307deab7e89b18da0b724c76638 +size 60695 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Not-set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Not-set-up.png index 65b9e9b4cb..17447ad8cd 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Not-set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Not-set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15ab57d4af84466b82204b0dbb737e38d2c705ccfade6371494842108458b1bd -size 82439 +oid sha256:6ba57bc91e2ca37583848b7127ebb0e6efc9f932cee136e596af6bd9f762490b +size 77531 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Set-up.png index 6f2bc885ab..8d002a493b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cf7b9fec4e99a3d71f9f9f704608f8209239ee1d0a2f89144a720be12bb1f433 -size 80121 +oid sha256:872d515fe6aae36c12d21a3e6757abcddf9e03e51e6421324505a528b5946376 +size 74557 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Unknown.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Unknown.png index 8a94879843..554a48fb7f 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Unknown.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Unknown.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a22acba2b438aeee6a26703ffafd346972c5ac764834a27b0542fed65298109d -size 37602 +oid sha256:bcf8ffb5e43fa810a9f6b4bdbcd837bde6263923a13d6a7d961a494cccace95f +size 37582 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Generating.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Generating.png new file mode 100644 index 0000000000..da61400cbf --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Generating.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3dee45d086663e3348733885a809a68e3f86cc33c70ca4e49676daa04f8dcd02 +size 100956 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Incomplete.png index 562b54e734..15c8ec4b63 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d67b8defaedb1ebd72d37fe40b52f70005cc1104be0e0d67a703c56fe337f98f -size 81282 +oid sha256:ea13695902f6a63b2ccac8fa1c9de60d15091bc40274482383d09464c3d31e7d +size 80944 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Not-set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Not-set-up.png index 9a5e34fb3d..633281d707 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Not-set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Not-set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a56bda1df5ee99fda29529826a288747634a1922b4bc4bcf544fd86a9e2097fc -size 118514 +oid sha256:d6506e821995ed525758a557e0dc64c57c3331267c37cd4bcea742fc244cc435 +size 106296 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Set-up.png index 541fb03459..09ee043125 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c10cc0a97a467368cac68fac0c2a67f1a68050e755349c7b008ba1bd7098853 -size 114487 +oid sha256:88b07c92a3f9763eb361918da508af0e140bce7241ebbc92c38f3ac38cc9394d +size 100293 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Unknown.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Unknown.png index 2d9df0b7f1..5403ae500b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Unknown.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Unknown.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6122ab324601debec8c3374c937852d0fef80951ad3771fa06f8b8db914bef18 -size 42478 +oid sha256:79fe4f39dcbd5438aa2d673e50e3efe24e2c5a4ea862684ff314c5d855ea486d +size 41923 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Both-setup.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Both-setup.png index c4154f28fd..f720b5833a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Both-setup.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Both-setup.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3afffdf37c3f24cc5c8c331faac191d39e4838ec21c6695702f38b625888b937 -size 105856 +oid sha256:41207583349f9c70c02e9f37482445511c597f46d150f6a2c289f0fd731c580b +size 138178 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Key-backup-disabled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Key-backup-disabled.png index 6e6b1e1926..7eeb523ce1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Key-backup-disabled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Key-backup-disabled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f262cd4362c5ea4d12f341badee0e253bb40c572fe5c9a8f0c8e24e8ae97b1e -size 98930 +oid sha256:4574b56fd9c50e2146d6675881dd544e00d9f6ca4b8fa30beed4d42160f833f1 +size 115816 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Only-key-backup-setup.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Only-key-backup-setup.png index 78bc3ab5cc..b3ac83ec0a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Only-key-backup-setup.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Only-key-backup-setup.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2f5bbe38921c7ea09aa97c0a50fb73342dd7594fdabfd758b23cbc02d3f4403 -size 117500 +oid sha256:0b2432ac9ec6980dfa6618409a401d3a18474c73e37f49b5e29ba3b0dd1d0da0 +size 134492 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Both-setup.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Both-setup.png index 7776834214..be34d219c4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Both-setup.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Both-setup.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:573167b53caef5932d4f65bde0f5b2464bcfe8fe1321215af93ab5df82ad460f -size 115876 +oid sha256:53c980680e3a15a67fb5bea95819d42da84dac6c15d346dec0e6e79107a3f3fe +size 169146 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Key-backup-disabled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Key-backup-disabled.png index 3e990fdee7..56b4e39deb 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Key-backup-disabled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Key-backup-disabled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a6cf656cead4fb620fd4fb9acd0d4038f0e75de1a9c3963d5394907ccb33664 -size 107128 +oid sha256:bc7cb971fef0b09290cd1296b67df22d792634a0f7ff48121e2432e1541ecfff +size 133858 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Only-key-backup-setup.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Only-key-backup-setup.png index 24e5cbf577..ac0323fc8e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Only-key-backup-setup.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Only-key-backup-setup.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b04cdd32066178398a510f3300457171f6b020a8e37aaf30facd743618cf3e2e -size 137128 +oid sha256:b08d094fbf0ca9f671a804a6894659ad9de40f10d376b1903f0449240dd0cf17 +size 163516 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Both-setup.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Both-setup.png index 3a84981783..023226e679 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Both-setup.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Both-setup.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4e365693e03873237cd2ce745731b4e41470dc2601e6ffa807da65ff7e2d206 -size 57067 +oid sha256:d297f54924748f5aa9d03852553f953647b2d18437a0e598371796fd006f33f5 +size 93416 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Key-backup-disabled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Key-backup-disabled.png index 38bfa7c7b5..cef726f648 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Key-backup-disabled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Key-backup-disabled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f51290abdc155242eafc3918e2bb3c52c7d7b1c93a0f9aea86688eba14e4f79 -size 50331 +oid sha256:cdef08ee53dcd8b6ba7f599e6264b17ca23add1cc0c824d5bb01ba033223920c +size 70563 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Only-key-backup-setup.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Only-key-backup-setup.png index 1cc3a66e26..53943bc2c4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Only-key-backup-setup.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Only-key-backup-setup.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:57e926d66d843ae875930a5625cd7fbf3ec73d2fced400cc476ba2d5e30e456e -size 70109 +oid sha256:9cdf64b4ce3eedf3b3c9dc78adecf093d562dd839ff94b34550602592d77f6ad +size 89304 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Both-setup.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Both-setup.png index 822706eef1..076fe71647 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Both-setup.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Both-setup.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c2397e68ef6f1666a4b8da11d2a84306a3bd162e0464cc1d3c344eee39cb58db -size 73528 +oid sha256:96b19939706086621b14c3b28dc7cc6cff5f92363f241e4efd907cbc82aa1ef1 +size 145495 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Key-backup-disabled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Key-backup-disabled.png index 8b86fada2a..948ff62d83 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Key-backup-disabled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Key-backup-disabled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49badfad4daefe3f88eb85716cde43a3a7e81587b61d7f27551cee2d8c7c3134 -size 62528 +oid sha256:d5bfb165c7566fa93c490d775c4d1ce1eed9a78bf78ad2ff7e3029e48684cca1 +size 99065 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Only-key-backup-setup.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Only-key-backup-setup.png index 4185dc2208..e988640be4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Only-key-backup-setup.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Only-key-backup-setup.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa050f41d2c83f5d441011ac88a47680913f1489dcc47500d816ce797531a0e3 -size 98235 +oid sha256:c08b79a27496e4c14e6a20b3dcc986cd4ac028ad9fbc1560ce331b3b03b1f9c6 +size 135286 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPad-en-GB.1.png index 522f6a5846..39b99fcd79 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66bf3b99fa39402357a04ebc6d403e77d5aea8cb89022f15506eb0ef64fe65ec -size 166144 +oid sha256:19c22b28087c5d9e3ebbde646f9acea9eeb522b4cd472728866ff55eb1f5560e +size 165392 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPad-pseudo.1.png index 87d3d450a3..808cbb1445 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3e2262a9b8752607b64d5e96a999eb5667c5dd4506f3be1374c59054a4fece5 -size 174257 +oid sha256:7f368c979f72fd2b11236d6dc38bf14ea7a3ea1659a50017678f95a97ef31813 +size 173122 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPhone-16-en-GB.1.png index 44b007a17c..2d0e9c06bc 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1062bd3adf6d606bb0d1f8c693bff1e66ae6110e0bf5a6f65037f2b980e6e39e -size 111198 +oid sha256:3becfb5155931fcb110b31ab68ae8079223eb5465094e77a54ab8dd04b0f7a57 +size 110465 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPhone-16-pseudo.1.png index 8703632b7e..e4c5aaaae9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_settingsScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a3e452e09cb3ab4908ad93c1802ceae66dc60334a2494fce8c34ea58bc2ab7e -size 127950 +oid sha256:8888662b7e7502bf76b721a09e4a978a44d8b9cad0384b19d642074e4a9101b2 +size 126872 From 7c75498b4dafe00ed63078d725d10a164a5fd5c7 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 28 Oct 2024 12:29:31 +0000 Subject: [PATCH 087/114] Replace individual RoomProxy properties with a stored RoomInfo. (#3445) * Store RoomInfo updates in JoinedRoomProxy and read from them directly. * Remove all RoomProxy properties that were reading from the RoomInfo. --- ElementX.xcodeproj/project.pbxproj | 4 + .../RoomFlowCoordinator.swift | 6 +- .../Mocks/Generated/GeneratedMocks.swift | 209 ++---------------- .../Sources/Mocks/InvitedRoomProxyMock.swift | 56 ++++- .../Sources/Mocks/JoinedRoomProxyMock.swift | 61 +++-- .../Sources/Mocks/KnockedRoomProxyMock.swift | 41 +++- .../CallScreen/CallScreenViewModel.swift | 3 +- .../HomeScreen/HomeScreenViewModel.swift | 8 +- .../JoinRoomScreenViewModel.swift | 28 +-- .../RoomChangeRolesScreenViewModel.swift | 3 +- .../RoomDetailsEditScreenViewModel.swift | 6 +- .../RoomDetailsScreenViewModel.swift | 38 ++-- .../RoomMembersListScreenViewModel.swift | 4 +- ...mNotificationSettingsScreenViewModel.swift | 6 +- ...omRolesAndPermissionsScreenViewModel.swift | 6 +- .../CompletionSuggestionService.swift | 2 +- .../RoomScreen/RoomScreenViewModel.swift | 27 ++- ...ificationSettingsEditScreenViewModel.swift | 2 +- .../Screens/Timeline/TimelineViewModel.swift | 25 +-- .../Sources/Services/Client/ClientProxy.swift | 14 +- .../ElementCall/ElementCallService.swift | 11 +- .../Services/Room/InvitedRoomProxy.swift | 61 +---- .../Services/Room/JoinedRoomProxy.swift | 98 ++------ .../Services/Room/KnockedRoomProxy.swift | 55 +---- .../Sources/Services/Room/RoomInfoProxy.swift | 56 +++++ .../Services/Room/RoomProxyProtocol.swift | 45 +--- .../RoomTimelineController.swift | 2 +- .../Sources/RoomScreenViewModelTests.swift | 26 ++- .../Sources/TimelineViewModelTests.swift | 30 ++- 29 files changed, 353 insertions(+), 580 deletions(-) create mode 100644 ElementX/Sources/Services/Room/RoomInfoProxy.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index d762d3b043..62b5b0931c 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -412,6 +412,7 @@ 5BC6C4ADFE7F2A795ECDE130 /* SecureBackupKeyBackupScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2D4EEBE8C098BBADD10939 /* SecureBackupKeyBackupScreenCoordinator.swift */; }; 5C02841B2A86327B2C377682 /* NotificationConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C830A64609CBD152F06E0457 /* NotificationConstants.swift */; }; 5C164551F7D26E24F09083D3 /* StaticLocationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C616D90B1E2F033CAA325439 /* StaticLocationScreenViewModelProtocol.swift */; }; + 5C8804B4F25903516E2DAB81 /* RoomInfoProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A66E8BC8D9AE4A08EFB2DF /* RoomInfoProxy.swift */; }; 5D27B6537591471A42C89027 /* EmoteRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 450E04B2A976CC4C8CC1807C /* EmoteRoomTimelineItem.swift */; }; 5D52925FEB1B780C65B0529F /* PinnedEventsTimelineScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA4F6D7000EDCD187E0989E7 /* PinnedEventsTimelineScreen.swift */; }; 5D53AE9342A4C06B704247ED /* MediaLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A02406480C351B8C6E0682C /* MediaLoaderProtocol.swift */; }; @@ -1495,6 +1496,7 @@ 40076C770A5FB83325252973 /* VoiceMessageMediaManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageMediaManager.swift; sourceTree = ""; }; 40316EFFEAC7B206EE9A55AE /* SecureBackupScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupScreenViewModelTests.swift; sourceTree = ""; }; 406C90AF8C3E98DF5D4E5430 /* ElementCallServiceConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallServiceConstants.swift; sourceTree = ""; }; + 40A66E8BC8D9AE4A08EFB2DF /* RoomInfoProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomInfoProxy.swift; sourceTree = ""; }; 40B21E611DADDEF00307E7AC /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; 4100DDE6BF3C566AB66B80CC /* MentionSuggestionItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionSuggestionItemView.swift; sourceTree = ""; }; 4137900E28201C314C835C11 /* RoomScreenFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenFooterView.swift; sourceTree = ""; }; @@ -3211,6 +3213,7 @@ 07C6B0B087FE6601C3F77816 /* JoinedRoomProxy.swift */, 858DA81F2ACF484B7CAD6AE4 /* KnockedRoomProxy.swift */, B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */, + 40A66E8BC8D9AE4A08EFB2DF /* RoomInfoProxy.swift */, 974AEAF3FE0C577A6C04AD6E /* RoomPermissions.swift */, 47111410B6E659A697D472B5 /* RoomProxyProtocol.swift */, 2C0F49BD446849654C0D24E0 /* RoomMember */, @@ -6775,6 +6778,7 @@ 42F1C8731166633E35A6D7E6 /* RoomEventStringBuilder.swift in Sources */, D55AF9B5B55FEED04771A461 /* RoomFlowCoordinator.swift in Sources */, 9C63171267E22FEB288EC860 /* RoomHeaderView.swift in Sources */, + 5C8804B4F25903516E2DAB81 /* RoomInfoProxy.swift in Sources */, 8A83D715940378B9BA9F739E /* RoomInviterLabel.swift in Sources */, F4996C82A4B3A5FF0C8EDD03 /* RoomListFilterModels.swift in Sources */, 4A9CEEE612D6D8B3DDBD28BA /* RoomListFilterView.swift in Sources */, diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index b5af3ad085..89faaa1c86 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -584,7 +584,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { timelineItemFactory: timelineItemFactory) self.timelineController = timelineController - analytics.trackViewRoom(isDM: roomProxy.isDirect, isSpace: roomProxy.isSpace) + analytics.trackViewRoom(isDM: roomProxy.infoPublisher.value.isDirect, isSpace: roomProxy.infoPublisher.value.isSpace) let completionSuggestionService = CompletionSuggestionService(roomProxy: roomProxy) @@ -681,7 +681,9 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { await storeAndSubscribeToRoomProxy(roomProxy) stateMachine.tryEvent(.presentRoom(focussedEvent: nil), userInfo: EventUserInfo(animated: animated)) - analytics.trackJoinedRoom(isDM: roomProxy.isDirect, isSpace: roomProxy.isSpace, activeMemberCount: UInt(roomProxy.activeMembersCount)) + analytics.trackJoinedRoom(isDM: roomProxy.infoPublisher.value.isDirect, + isSpace: roomProxy.infoPublisher.value.isSpace, + activeMemberCount: UInt(roomProxy.infoPublisher.value.activeMembersCount)) } else { stateMachine.tryEvent(.dismissFlow, userInfo: EventUserInfo(animated: animated)) } diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 9755771e0b..2438f6b7f3 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -5824,67 +5824,21 @@ class ElementCallWidgetDriverMock: ElementCallWidgetDriverProtocol { } } class InvitedRoomProxyMock: InvitedRoomProxyProtocol { - var inviterCallsCount = 0 - var inviterCalled: Bool { - return inviterCallsCount > 0 + var info: RoomInfoProxy { + get { return underlyingInfo } + set(value) { underlyingInfo = value } } - - var inviter: RoomMemberProxyProtocol? { - get async { - inviterCallsCount += 1 - if let inviterClosure = inviterClosure { - return await inviterClosure() - } else { - return underlyingInviter - } - } - } - var underlyingInviter: RoomMemberProxyProtocol? - var inviterClosure: (() async -> RoomMemberProxyProtocol?)? + var underlyingInfo: RoomInfoProxy! var id: String { get { return underlyingId } set(value) { underlyingId = value } } var underlyingId: String! - var canonicalAlias: String? var ownUserID: String { get { return underlyingOwnUserID } set(value) { underlyingOwnUserID = value } } var underlyingOwnUserID: String! - var name: String? - var topic: String? - var avatar: RoomAvatar { - get { return underlyingAvatar } - set(value) { underlyingAvatar = value } - } - var underlyingAvatar: RoomAvatar! - var avatarURL: URL? - var isPublic: Bool { - get { return underlyingIsPublic } - set(value) { underlyingIsPublic = value } - } - var underlyingIsPublic: Bool! - var isDirect: Bool { - get { return underlyingIsDirect } - set(value) { underlyingIsDirect = value } - } - var underlyingIsDirect: Bool! - var isSpace: Bool { - get { return underlyingIsSpace } - set(value) { underlyingIsSpace = value } - } - var underlyingIsSpace: Bool! - var joinedMembersCount: Int { - get { return underlyingJoinedMembersCount } - set(value) { underlyingJoinedMembersCount = value } - } - var underlyingJoinedMembersCount: Int! - var activeMembersCount: Int { - get { return underlyingActiveMembersCount } - set(value) { underlyingActiveMembersCount = value } - } - var underlyingActiveMembersCount: Int! //MARK: - rejectInvitation @@ -6021,46 +5975,11 @@ class JoinedRoomProxyMock: JoinedRoomProxyProtocol { set(value) { underlyingIsEncrypted = value } } var underlyingIsEncrypted: Bool! - var isFavouriteCallsCount = 0 - var isFavouriteCalled: Bool { - return isFavouriteCallsCount > 0 - } - - var isFavourite: Bool { - get async { - isFavouriteCallsCount += 1 - if let isFavouriteClosure = isFavouriteClosure { - return await isFavouriteClosure() - } else { - return underlyingIsFavourite - } - } - } - var underlyingIsFavourite: Bool! - var isFavouriteClosure: (() async -> Bool)? - var pinnedEventIDsCallsCount = 0 - var pinnedEventIDsCalled: Bool { - return pinnedEventIDsCallsCount > 0 + var infoPublisher: CurrentValuePublisher { + get { return underlyingInfoPublisher } + set(value) { underlyingInfoPublisher = value } } - - var pinnedEventIDs: Set { - get async { - pinnedEventIDsCallsCount += 1 - if let pinnedEventIDsClosure = pinnedEventIDsClosure { - return await pinnedEventIDsClosure() - } else { - return underlyingPinnedEventIDs - } - } - } - var underlyingPinnedEventIDs: Set! - var pinnedEventIDsClosure: (() async -> Set)? - var hasOngoingCall: Bool { - get { return underlyingHasOngoingCall } - set(value) { underlyingHasOngoingCall = value } - } - var underlyingHasOngoingCall: Bool! - var activeRoomCallParticipants: [String] = [] + var underlyingInfoPublisher: CurrentValuePublisher! var membersPublisher: CurrentValuePublisher<[RoomMemberProxyProtocol], Never> { get { return underlyingMembersPublisher } set(value) { underlyingMembersPublisher = value } @@ -6076,11 +5995,6 @@ class JoinedRoomProxyMock: JoinedRoomProxyProtocol { set(value) { underlyingIdentityStatusChangesPublisher = value } } var underlyingIdentityStatusChangesPublisher: CurrentValuePublisher<[IdentityStatusChange], Never>! - var actionsPublisher: AnyPublisher { - get { return underlyingActionsPublisher } - set(value) { underlyingActionsPublisher = value } - } - var underlyingActionsPublisher: AnyPublisher! var timeline: TimelineProxyProtocol { get { return underlyingTimeline } set(value) { underlyingTimeline = value } @@ -6108,45 +6022,11 @@ class JoinedRoomProxyMock: JoinedRoomProxyProtocol { set(value) { underlyingId = value } } var underlyingId: String! - var canonicalAlias: String? var ownUserID: String { get { return underlyingOwnUserID } set(value) { underlyingOwnUserID = value } } var underlyingOwnUserID: String! - var name: String? - var topic: String? - var avatar: RoomAvatar { - get { return underlyingAvatar } - set(value) { underlyingAvatar = value } - } - var underlyingAvatar: RoomAvatar! - var avatarURL: URL? - var isPublic: Bool { - get { return underlyingIsPublic } - set(value) { underlyingIsPublic = value } - } - var underlyingIsPublic: Bool! - var isDirect: Bool { - get { return underlyingIsDirect } - set(value) { underlyingIsDirect = value } - } - var underlyingIsDirect: Bool! - var isSpace: Bool { - get { return underlyingIsSpace } - set(value) { underlyingIsSpace = value } - } - var underlyingIsSpace: Bool! - var joinedMembersCount: Int { - get { return underlyingJoinedMembersCount } - set(value) { underlyingJoinedMembersCount = value } - } - var underlyingJoinedMembersCount: Int! - var activeMembersCount: Int { - get { return underlyingActiveMembersCount } - set(value) { underlyingActiveMembersCount = value } - } - var underlyingActiveMembersCount: Int! //MARK: - subscribeForUpdates @@ -9752,50 +9632,21 @@ class KeychainControllerMock: KeychainControllerProtocol { } } class KnockedRoomProxyMock: KnockedRoomProxyProtocol { + var info: RoomInfoProxy { + get { return underlyingInfo } + set(value) { underlyingInfo = value } + } + var underlyingInfo: RoomInfoProxy! var id: String { get { return underlyingId } set(value) { underlyingId = value } } var underlyingId: String! - var canonicalAlias: String? var ownUserID: String { get { return underlyingOwnUserID } set(value) { underlyingOwnUserID = value } } var underlyingOwnUserID: String! - var name: String? - var topic: String? - var avatar: RoomAvatar { - get { return underlyingAvatar } - set(value) { underlyingAvatar = value } - } - var underlyingAvatar: RoomAvatar! - var avatarURL: URL? - var isPublic: Bool { - get { return underlyingIsPublic } - set(value) { underlyingIsPublic = value } - } - var underlyingIsPublic: Bool! - var isDirect: Bool { - get { return underlyingIsDirect } - set(value) { underlyingIsDirect = value } - } - var underlyingIsDirect: Bool! - var isSpace: Bool { - get { return underlyingIsSpace } - set(value) { underlyingIsSpace = value } - } - var underlyingIsSpace: Bool! - var joinedMembersCount: Int { - get { return underlyingJoinedMembersCount } - set(value) { underlyingJoinedMembersCount = value } - } - var underlyingJoinedMembersCount: Int! - var activeMembersCount: Int { - get { return underlyingActiveMembersCount } - set(value) { underlyingActiveMembersCount = value } - } - var underlyingActiveMembersCount: Int! //MARK: - cancelKnock @@ -12934,45 +12785,11 @@ class RoomProxyMock: RoomProxyProtocol { set(value) { underlyingId = value } } var underlyingId: String! - var canonicalAlias: String? var ownUserID: String { get { return underlyingOwnUserID } set(value) { underlyingOwnUserID = value } } var underlyingOwnUserID: String! - var name: String? - var topic: String? - var avatar: RoomAvatar { - get { return underlyingAvatar } - set(value) { underlyingAvatar = value } - } - var underlyingAvatar: RoomAvatar! - var avatarURL: URL? - var isPublic: Bool { - get { return underlyingIsPublic } - set(value) { underlyingIsPublic = value } - } - var underlyingIsPublic: Bool! - var isDirect: Bool { - get { return underlyingIsDirect } - set(value) { underlyingIsDirect = value } - } - var underlyingIsDirect: Bool! - var isSpace: Bool { - get { return underlyingIsSpace } - set(value) { underlyingIsSpace = value } - } - var underlyingIsSpace: Bool! - var joinedMembersCount: Int { - get { return underlyingJoinedMembersCount } - set(value) { underlyingJoinedMembersCount = value } - } - var underlyingJoinedMembersCount: Int! - var activeMembersCount: Int { - get { return underlyingActiveMembersCount } - set(value) { underlyingActiveMembersCount = value } - } - var underlyingActiveMembersCount: Int! } class RoomSummaryProviderMock: RoomSummaryProviderProtocol { diff --git a/ElementX/Sources/Mocks/InvitedRoomProxyMock.swift b/ElementX/Sources/Mocks/InvitedRoomProxyMock.swift index 5155a6febd..66bc9a93f2 100644 --- a/ElementX/Sources/Mocks/InvitedRoomProxyMock.swift +++ b/ElementX/Sources/Mocks/InvitedRoomProxyMock.swift @@ -7,6 +7,7 @@ import Combine import Foundation +import MatrixRustSDK @MainActor struct InvitedRoomProxyMockConfiguration { @@ -22,10 +23,55 @@ extension InvitedRoomProxyMock { convenience init(_ configuration: InvitedRoomProxyMockConfiguration) { self.init() id = configuration.id - name = configuration.name - avatarURL = configuration.avatarURL - avatar = .room(id: configuration.id, name: configuration.name, avatarURL: configuration.avatarURL) // Note: This doesn't replicate the real proxy logic. - underlyingInviter = configuration.inviter - activeMembersCount = configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count + info = RoomInfoProxy(roomInfo: .init(configuration)) + } +} + +extension RoomInfo { + @MainActor init(_ configuration: InvitedRoomProxyMockConfiguration) { + self.init(id: configuration.id, + creator: nil, + displayName: configuration.name, + rawName: nil, + topic: nil, + avatarUrl: configuration.avatarURL?.absoluteString, + isDirect: false, + isPublic: false, + isSpace: false, + isTombstoned: false, + isFavourite: false, + canonicalAlias: nil, + alternativeAliases: [], + membership: .knocked, + inviter: .init(configuration.inviter), + heroes: [], + activeMembersCount: UInt64(configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count), + invitedMembersCount: UInt64(configuration.members.filter { $0.membership == .invite }.count), + joinedMembersCount: UInt64(configuration.members.filter { $0.membership == .join }.count), + userPowerLevels: [:], + highlightCount: 0, + notificationCount: 0, + cachedUserDefinedNotificationMode: nil, + hasRoomCall: false, + activeRoomCallParticipants: [], + isMarkedUnread: false, + numUnreadMessages: 0, + numUnreadNotifications: 0, + numUnreadMentions: 0, + pinnedEventIds: []) + } +} + +private extension RoomMember { + init(_ proxy: RoomMemberProxyProtocol) { + self.init(userId: proxy.userID, + displayName: proxy.displayName, + avatarUrl: proxy.avatarURL?.absoluteString, + membership: proxy.membership, + isNameAmbiguous: proxy.disambiguatedDisplayName != proxy.displayName, + powerLevel: Int64(proxy.powerLevel), + normalizedPowerLevel: Int64(proxy.powerLevel), + isIgnored: proxy.isIgnored, + suggestedRoleForPowerLevel: proxy.role) } } diff --git a/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift b/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift index 0855235a08..2f147104fb 100644 --- a/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift +++ b/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift @@ -7,6 +7,7 @@ import Combine import Foundation +import MatrixRustSDK enum RoomProxyMockError: Error { case generic @@ -46,18 +47,7 @@ extension JoinedRoomProxyMock { self.init() id = configuration.id - name = configuration.name - topic = configuration.topic - avatar = .room(id: configuration.id, name: configuration.name, avatarURL: configuration.avatarURL) // Note: This doesn't replicate the real proxy logic. - avatarURL = configuration.avatarURL - isDirect = configuration.isDirect - isSpace = configuration.isSpace - isPublic = configuration.isPublic isEncrypted = configuration.isEncrypted - hasOngoingCall = configuration.hasOngoingCall - canonicalAlias = configuration.canonicalAlias - - underlyingPinnedEventIDs = configuration.pinnedEventIDs let timeline = TimelineProxyMock() timeline.sendMessageEventContentReturnValue = .success(()) @@ -78,15 +68,12 @@ extension JoinedRoomProxyMock { ownUserID = configuration.ownUserID + infoPublisher = CurrentValueSubject(.init(roomInfo: .init(configuration))).asCurrentValuePublisher() membersPublisher = CurrentValueSubject(configuration.members).asCurrentValuePublisher() typingMembersPublisher = CurrentValueSubject([]).asCurrentValuePublisher() identityStatusChangesPublisher = CurrentValueSubject([]).asCurrentValuePublisher() - - joinedMembersCount = configuration.members.filter { $0.membership == .join }.count - activeMembersCount = configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count updateMembersClosure = { } - underlyingActionsPublisher = Empty(completeImmediately: false).eraseToAnyPublisher() setNameClosure = { _ in .success(()) } setTopicClosure = { _ in .success(()) } getMemberUserIDClosure = { [weak self] userID in @@ -102,7 +89,6 @@ extension JoinedRoomProxyMock { flagAsUnreadReturnValue = .success(()) markAsReadReceiptTypeReturnValue = .success(()) - underlyingIsFavourite = false flagAsFavouriteReturnValue = .success(()) powerLevelsReturnValue = .success(.mock) @@ -154,3 +140,46 @@ extension JoinedRoomProxyMock { clearDraftReturnValue = .success(()) } } + +extension RoomInfo { + @MainActor init(_ configuration: JoinedRoomProxyMockConfiguration) { + self.init(id: configuration.id, + creator: nil, + displayName: configuration.name, + rawName: configuration.name, + topic: configuration.topic, + avatarUrl: configuration.avatarURL?.absoluteString, + isDirect: configuration.isDirect, + isPublic: configuration.isPublic, + isSpace: configuration.isSpace, + isTombstoned: false, + isFavourite: false, + canonicalAlias: configuration.canonicalAlias, + alternativeAliases: [], + membership: .joined, + inviter: configuration.inviter.map { RoomMember(userId: $0.userID, + displayName: $0.displayName, + avatarUrl: $0.avatarURL?.absoluteString, + membership: $0.membership, + isNameAmbiguous: false, + powerLevel: Int64($0.powerLevel), + normalizedPowerLevel: Int64($0.powerLevel), + isIgnored: $0.isIgnored, + suggestedRoleForPowerLevel: $0.role) }, + heroes: [], + activeMembersCount: UInt64(configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count), + invitedMembersCount: UInt64(configuration.members.filter { $0.membership == .invite }.count), + joinedMembersCount: UInt64(configuration.members.filter { $0.membership == .join }.count), + userPowerLevels: [:], + highlightCount: 0, + notificationCount: 0, + cachedUserDefinedNotificationMode: .allMessages, + hasRoomCall: configuration.hasOngoingCall, + activeRoomCallParticipants: [], + isMarkedUnread: false, + numUnreadMessages: 0, + numUnreadNotifications: 0, + numUnreadMentions: 0, + pinnedEventIds: Array(configuration.pinnedEventIDs)) + } +} diff --git a/ElementX/Sources/Mocks/KnockedRoomProxyMock.swift b/ElementX/Sources/Mocks/KnockedRoomProxyMock.swift index 610e975a57..76559bae13 100644 --- a/ElementX/Sources/Mocks/KnockedRoomProxyMock.swift +++ b/ElementX/Sources/Mocks/KnockedRoomProxyMock.swift @@ -7,6 +7,7 @@ import Combine import Foundation +import MatrixRustSDK @MainActor struct KnockedRoomProxyMockConfiguration { @@ -21,9 +22,41 @@ extension KnockedRoomProxyMock { convenience init(_ configuration: KnockedRoomProxyMockConfiguration) { self.init() id = configuration.id - name = configuration.name - avatarURL = configuration.avatarURL - avatar = .room(id: configuration.id, name: configuration.name, avatarURL: configuration.avatarURL) // Note: This doesn't replicate the real proxy logic. - activeMembersCount = configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count + info = RoomInfoProxy(roomInfo: .init(configuration)) + } +} + +extension RoomInfo { + @MainActor init(_ configuration: KnockedRoomProxyMockConfiguration) { + self.init(id: configuration.id, + creator: nil, + displayName: configuration.name, + rawName: nil, + topic: nil, + avatarUrl: configuration.avatarURL?.absoluteString, + isDirect: false, + isPublic: false, + isSpace: false, + isTombstoned: false, + isFavourite: false, + canonicalAlias: nil, + alternativeAliases: [], + membership: .knocked, + inviter: nil, + heroes: [], + activeMembersCount: UInt64(configuration.members.filter { $0.membership == .join || $0.membership == .invite }.count), + invitedMembersCount: UInt64(configuration.members.filter { $0.membership == .invite }.count), + joinedMembersCount: UInt64(configuration.members.filter { $0.membership == .join }.count), + userPowerLevels: [:], + highlightCount: 0, + notificationCount: 0, + cachedUserDefinedNotificationMode: nil, + hasRoomCall: false, + activeRoomCallParticipants: [], + isMarkedUnread: false, + numUnreadMessages: 0, + numUnreadNotifications: 0, + numUnreadMentions: 0, + pinnedEventIds: []) } } diff --git a/ElementX/Sources/Screens/CallScreen/CallScreenViewModel.swift b/ElementX/Sources/Screens/CallScreen/CallScreenViewModel.swift index 1e9e0315aa..4c41b43586 100644 --- a/ElementX/Sources/Screens/CallScreen/CallScreenViewModel.swift +++ b/ElementX/Sources/Screens/CallScreen/CallScreenViewModel.swift @@ -170,7 +170,8 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol return } - await elementCallService.setupCallSession(roomID: roomProxy.id, roomDisplayName: roomProxy.roomTitle) + await elementCallService.setupCallSession(roomID: roomProxy.id, + roomDisplayName: roomProxy.infoPublisher.value.displayName ?? roomProxy.id) _ = await roomProxy.sendCallNotificationIfNeeded() diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index 7b40e72d23..b689ce6ca1 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -360,10 +360,10 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol return } - if roomProxy.isPublic { + if roomProxy.infoPublisher.value.isPublic { state.bindings.leaveRoomAlertItem = LeaveRoomAlertItem(roomID: roomID, isDM: roomProxy.isEncryptedOneToOneRoom, state: .public) } else { - state.bindings.leaveRoomAlertItem = if roomProxy.joinedMembersCount > 1 { + state.bindings.leaveRoomAlertItem = if roomProxy.infoPublisher.value.joinedMembersCount > 1 { LeaveRoomAlertItem(roomID: roomID, isDM: roomProxy.isEncryptedOneToOneRoom, state: .private) } else { LeaveRoomAlertItem(roomID: roomID, isDM: roomProxy.isEncryptedOneToOneRoom, state: .empty) @@ -408,7 +408,9 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol switch await roomProxy.acceptInvitation() { case .success: actionsSubject.send(.presentRoom(roomIdentifier: roomID)) - analyticsService.trackJoinedRoom(isDM: roomProxy.isDirect, isSpace: roomProxy.isSpace, activeMemberCount: UInt(roomProxy.activeMembersCount)) + analyticsService.trackJoinedRoom(isDM: roomProxy.info.isDirect, + isSpace: roomProxy.info.isSpace, + activeMemberCount: UInt(roomProxy.info.activeMembersCount)) case .failure: displayError() } diff --git a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift index b97b85bd7e..2d88bfb14e 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift @@ -74,7 +74,7 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo defer { hideLoadingIndicator() - Task { await updateRoomDetails() } + updateRoomDetails() } await updateRoom() @@ -82,7 +82,7 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo switch await clientProxy.roomPreviewForIdentifier(roomID, via: via) { case .success(let roomPreviewDetails): self.roomPreviewDetails = roomPreviewDetails - await updateRoomDetails() + updateRoomDetails() case .failure(.roomPreviewIsPrivate): break // Handled by the mode, we don't need an error indicator. case .failure: @@ -97,32 +97,32 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo // take priority over the preview one. if let room = await clientProxy.roomForIdentifier(roomID) { self.room = room - await updateRoomDetails() + updateRoomDetails() } } - private func updateRoomDetails() async { - var roomProxy: RoomProxyProtocol? + private func updateRoomDetails() { + var roomInfo: RoomInfoProxy? var inviter: RoomInviterDetails? switch room { case .joined(let joinedRoomProxy): - roomProxy = joinedRoomProxy + roomInfo = joinedRoomProxy.infoPublisher.value case .invited(let invitedRoomProxy): - inviter = await invitedRoomProxy.inviter.flatMap(RoomInviterDetails.init) - roomProxy = invitedRoomProxy + inviter = invitedRoomProxy.info.inviter.flatMap(RoomInviterDetails.init) + roomInfo = invitedRoomProxy.info case .knocked(let knockedRoomProxy): - roomProxy = knockedRoomProxy + roomInfo = knockedRoomProxy.info default: break } - let name = roomProxy?.name ?? roomPreviewDetails?.name + let name = roomInfo?.displayName ?? roomPreviewDetails?.name state.roomDetails = JoinRoomScreenRoomDetails(name: name, - topic: roomProxy?.topic ?? roomPreviewDetails?.topic, - canonicalAlias: roomProxy?.canonicalAlias ?? roomPreviewDetails?.canonicalAlias, - avatar: roomProxy?.avatar ?? .room(id: roomID, name: name ?? "", avatarURL: roomPreviewDetails?.avatarURL), - memberCount: UInt(roomProxy?.activeMembersCount ?? Int(roomPreviewDetails?.memberCount ?? 0)), + topic: roomInfo?.topic ?? roomPreviewDetails?.topic, + canonicalAlias: roomInfo?.canonicalAlias ?? roomPreviewDetails?.canonicalAlias, + avatar: roomInfo?.avatar ?? .room(id: roomID, name: name ?? "", avatarURL: roomPreviewDetails?.avatarURL), + memberCount: UInt(roomInfo?.activeMembersCount ?? Int(roomPreviewDetails?.memberCount ?? 0)), inviter: inviter) updateMode() diff --git a/ElementX/Sources/Screens/RoomChangeRolesScreen/RoomChangeRolesScreenViewModel.swift b/ElementX/Sources/Screens/RoomChangeRolesScreen/RoomChangeRolesScreenViewModel.swift index 984ce6cfea..d0b12c8f56 100644 --- a/ElementX/Sources/Screens/RoomChangeRolesScreen/RoomChangeRolesScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomChangeRolesScreen/RoomChangeRolesScreenViewModel.swift @@ -143,7 +143,8 @@ class RoomChangeRolesScreenViewModel: RoomChangeRolesScreenViewModelType, RoomCh let demotingUpdates = state.membersToDemote.map { ($0.id, Int64(0)) } // A task we can await until the room's info gets modified with the new power levels. - let infoTask = Task { await roomProxy.actionsPublisher.values.first { $0 == .roomInfoUpdate } } + // Note: Ignore the first value as the publisher is backed by a current value subject. + let infoTask = Task { await roomProxy.infoPublisher.dropFirst().values.first { _ in true } } switch await roomProxy.updatePowerLevelsForUsers(promotingUpdates + demotingUpdates) { case .success: diff --git a/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenViewModel.swift b/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenViewModel.swift index cc16d6a709..b750cf7280 100644 --- a/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenViewModel.swift @@ -28,9 +28,9 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe self.mediaUploadingPreprocessor = mediaUploadingPreprocessor self.userIndicatorController = userIndicatorController - let roomAvatar = roomProxy.avatarURL - let roomName = roomProxy.name - let roomTopic = roomProxy.topic + let roomAvatar = roomProxy.infoPublisher.value.avatarURL + let roomName = roomProxy.infoPublisher.value.displayName + let roomTopic = roomProxy.infoPublisher.value.topic super.init(initialViewState: RoomDetailsEditScreenViewState(roomID: roomProxy.id, initialAvatarURL: roomAvatar, diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift index 55553d881f..d56498ef80 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift @@ -63,14 +63,14 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr self.attributedStringBuilder = attributedStringBuilder self.appSettings = appSettings - let topic = attributedStringBuilder.fromPlain(roomProxy.topic) + let topic = attributedStringBuilder.fromPlain(roomProxy.infoPublisher.value.topic) super.init(initialViewState: .init(details: roomProxy.details, isEncrypted: roomProxy.isEncrypted, - isDirect: roomProxy.isDirect, + isDirect: roomProxy.infoPublisher.value.isDirect, topic: topic, topicSummary: topic?.unattributedStringByReplacingNewlinesWithSpaces(), - joinedMembersCount: roomProxy.joinedMembersCount, + joinedMembersCount: roomProxy.infoPublisher.value.joinedMembersCount, notificationSettingsState: .loading, bindings: .init()), mediaProvider: mediaProvider) @@ -96,7 +96,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr } } - updateRoomInfo() + updateRoomInfo(roomProxy.infoPublisher.value) Task { await updatePowerLevelPermissions() } setupRoomSubscription() @@ -124,7 +124,9 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr state.bindings.leaveRoomAlertItem = LeaveRoomAlertItem(roomID: roomProxy.id, isDM: roomProxy.isEncryptedOneToOneRoom, state: .empty) return } - state.bindings.leaveRoomAlertItem = LeaveRoomAlertItem(roomID: roomProxy.id, isDM: roomProxy.isEncryptedOneToOneRoom, state: roomProxy.isPublic ? .public : .private) + state.bindings.leaveRoomAlertItem = LeaveRoomAlertItem(roomID: roomProxy.id, + isDM: roomProxy.isEncryptedOneToOneRoom, + state: roomProxy.infoPublisher.value.isPublic ? .public : .private) case .confirmLeave: Task { await leaveRoom() } case .processTapIgnore: @@ -162,29 +164,25 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr } // MARK: - Private - + private func setupRoomSubscription() { - roomProxy.actionsPublisher - .filter { $0 == .roomInfoUpdate } + roomProxy.infoPublisher .throttle(for: .milliseconds(200), scheduler: DispatchQueue.main, latest: true) - .sink { [weak self] _ in - self?.updateRoomInfo() + .sink { [weak self] roomInfo in + self?.updateRoomInfo(roomInfo) Task { await self?.updatePowerLevelPermissions() } } .store(in: &cancellables) } - private func updateRoomInfo() { + private func updateRoomInfo(_ roomInfo: RoomInfoProxy) { state.details = roomProxy.details - let topic = attributedStringBuilder.fromPlain(roomProxy.topic) + let topic = attributedStringBuilder.fromPlain(roomInfo.topic) state.topic = topic state.topicSummary = topic?.unattributedStringByReplacingNewlinesWithSpaces() - state.joinedMembersCount = roomProxy.joinedMembersCount - - Task { - state.bindings.isFavourite = await roomProxy.isFavourite - } + state.joinedMembersCount = roomInfo.joinedMembersCount + state.bindings.isFavourite = roomInfo.isFavourite } private func fetchMembersIfNeeded() async { @@ -240,7 +238,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr do { let notificationMode = try await notificationSettingsProxy.getNotificationSettings(roomId: roomProxy.id, isEncrypted: roomProxy.isEncrypted, - isOneToOne: roomProxy.activeMembersCount == 2) + isOneToOne: roomProxy.infoPublisher.value.activeMembersCount == 2) state.notificationSettingsState = .loaded(settings: notificationMode) } catch { state.notificationSettingsState = .error @@ -258,7 +256,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr do { try await notificationSettingsProxy.unmuteRoom(roomId: roomProxy.id, isEncrypted: roomProxy.isEncrypted, - isOneToOne: roomProxy.activeMembersCount == 2) + isOneToOne: roomProxy.infoPublisher.value.activeMembersCount == 2) } catch { state.bindings.alertInfo = AlertInfo(id: .alert, title: L10n.commonError, @@ -352,7 +350,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr // We don't actually know the mime type here, assume it's an image. if case let .success(file) = await mediaProvider.loadFileFromSource(.init(url: url, mimeType: "image/jpeg")) { - state.bindings.mediaPreviewItem = MediaPreviewItem(file: file, title: roomProxy.roomTitle) + state.bindings.mediaPreviewItem = MediaPreviewItem(file: file, title: roomProxy.infoPublisher.value.displayName) } } } diff --git a/ElementX/Sources/Screens/RoomMemberListScreen/RoomMembersListScreenViewModel.swift b/ElementX/Sources/Screens/RoomMemberListScreen/RoomMembersListScreenViewModel.swift index 129b8e8faa..a43afebdc6 100644 --- a/ElementX/Sources/Screens/RoomMemberListScreen/RoomMembersListScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomMemberListScreen/RoomMembersListScreenViewModel.swift @@ -32,7 +32,7 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe self.userIndicatorController = userIndicatorController self.analytics = analytics - super.init(initialViewState: .init(joinedMembersCount: roomProxy.joinedMembersCount, + super.init(initialViewState: .init(joinedMembersCount: roomProxy.infoPublisher.value.joinedMembersCount, bindings: .init(mode: initialMode)), mediaProvider: mediaProvider) @@ -92,7 +92,7 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe let members = members.sorted() let roomMembersDetails = await buildMembersDetails(members: members) self.members = members - self.state = .init(joinedMembersCount: roomProxy.joinedMembersCount, + self.state = .init(joinedMembersCount: roomProxy.infoPublisher.value.joinedMembersCount, joinedMembers: roomMembersDetails.joinedMembers, invitedMembers: roomMembersDetails.invitedMembers, bannedMembers: roomMembersDetails.bannedMembers, diff --git a/ElementX/Sources/Screens/RoomNotificationSettingsScreen/RoomNotificationSettingsScreenViewModel.swift b/ElementX/Sources/Screens/RoomNotificationSettingsScreen/RoomNotificationSettingsScreenViewModel.swift index 748f07caa5..0a09262325 100644 --- a/ElementX/Sources/Screens/RoomNotificationSettingsScreen/RoomNotificationSettingsScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomNotificationSettingsScreen/RoomNotificationSettingsScreenViewModel.swift @@ -26,11 +26,11 @@ class RoomNotificationSettingsScreenViewModel: RoomNotificationSettingsScreenVie let bindings = RoomNotificationSettingsScreenViewStateBindings() self.notificationSettingsProxy = notificationSettingsProxy self.roomProxy = roomProxy - let navigationTitle = displayAsUserDefinedRoomSettings ? roomProxy.roomTitle : L10n.screenRoomDetailsNotificationTitle + let navigationTitle = displayAsUserDefinedRoomSettings ? roomProxy.infoPublisher.value.displayName : L10n.screenRoomDetailsNotificationTitle let customSettingsSectionHeader = displayAsUserDefinedRoomSettings ? L10n.screenRoomNotificationSettingsRoomCustomSettingsTitle : L10n.screenRoomNotificationSettingsCustomSettingsTitle super.init(initialViewState: RoomNotificationSettingsScreenViewState(bindings: bindings, displayAsUserDefinedRoomSettings: displayAsUserDefinedRoomSettings, - navigationTitle: navigationTitle, + navigationTitle: navigationTitle ?? L10n.screenRoomDetailsNotificationTitle, customSettingsSectionHeader: customSettingsSectionHeader)) setupNotificationSettingsSubscription() @@ -80,7 +80,7 @@ class RoomNotificationSettingsScreenViewModel: RoomNotificationSettingsScreenVie // `isOneToOne` here is not the same as `isDirect` on the room. From the point of view of the push rule, a one-to-one room is a room with exactly two active members. let settings = try await notificationSettingsProxy.getNotificationSettings(roomId: roomProxy.id, isEncrypted: roomProxy.isEncrypted, - isOneToOne: roomProxy.activeMembersCount == 2) + isOneToOne: roomProxy.infoPublisher.value.activeMembersCount == 2) guard !Task.isCancelled else { return } state.notificationSettingsState = .loaded(settings: settings) if !state.isRestoringDefaultSetting { diff --git a/ElementX/Sources/Screens/RoomRolesAndPermissionsScreen/RoomRolesAndPermissionsScreenViewModel.swift b/ElementX/Sources/Screens/RoomRolesAndPermissionsScreen/RoomRolesAndPermissionsScreenViewModel.swift index 692a32698b..2c7a4a50da 100644 --- a/ElementX/Sources/Screens/RoomRolesAndPermissionsScreen/RoomRolesAndPermissionsScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomRolesAndPermissionsScreen/RoomRolesAndPermissionsScreenViewModel.swift @@ -37,8 +37,7 @@ class RoomRolesAndPermissionsScreenViewModel: RoomRolesAndPermissionsScreenViewM updateMembers(roomProxy.membersPublisher.value) // Automatically update the room permissions - roomProxy.actionsPublisher - .filter { $0 == .roomInfoUpdate } + roomProxy.infoPublisher .receive(on: DispatchQueue.main) .sink { [weak self] _ in Task { await self?.updatePermissions() } @@ -93,7 +92,8 @@ class RoomRolesAndPermissionsScreenViewModel: RoomRolesAndPermissionsScreenViewM showSavingIndicator() // A task we can await until the room's info gets modified with the new power levels. - let infoTask = Task { await roomProxy.actionsPublisher.values.first { $0 == .roomInfoUpdate } } + // Note: Ignore the first value as the publisher is backed by a current value subject. + let infoTask = Task { await roomProxy.infoPublisher.dropFirst().values.first { _ in true } } switch await roomProxy.updatePowerLevelsForUsers([(userID: roomProxy.ownUserID, powerLevel: role.rustPowerLevel)]) { case .success: diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/CompletionSuggestionService.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/CompletionSuggestionService.swift index de54046aa0..a084736de8 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/CompletionSuggestionService.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/CompletionSuggestionService.swift @@ -51,7 +51,7 @@ final class CompletionSuggestionService: CompletionSuggestionServiceProtocol { membersSuggestion .insert(SuggestionItem.allUsers(item: .init(id: PillConstants.atRoom, displayName: PillConstants.everyone, - avatarURL: self.roomProxy.avatarURL, + avatarURL: self.roomProxy.infoPublisher.value.avatarURL, range: suggestionTrigger.range)), at: 0) } diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index 36f3bb7239..93c95e1a3f 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -68,14 +68,14 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol self.initialSelectedPinnedEventID = initialSelectedPinnedEventID pinnedEventStringBuilder = .pinnedEventStringBuilder(userID: roomProxy.ownUserID) - super.init(initialViewState: .init(roomTitle: roomProxy.roomTitle, - roomAvatar: roomProxy.avatar, - hasOngoingCall: roomProxy.hasOngoingCall, + super.init(initialViewState: .init(roomTitle: roomProxy.infoPublisher.value.displayName ?? roomProxy.id, + roomAvatar: roomProxy.infoPublisher.value.avatar, + hasOngoingCall: roomProxy.infoPublisher.value.hasRoomCall, bindings: .init()), mediaProvider: mediaProvider) Task { - await handleRoomInfoUpdate() + await handleRoomInfoUpdate(roomProxy.infoPublisher.value) } setupSubscriptions(ongoingCallRoomIDPublisher: ongoingCallRoomIDPublisher) @@ -118,26 +118,25 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol private func setupSubscriptions(ongoingCallRoomIDPublisher: CurrentValuePublisher) { let roomInfoSubscription = roomProxy - .actionsPublisher - .filter { $0 == .roomInfoUpdate } + .infoPublisher roomInfoSubscription .throttle(for: .seconds(1), scheduler: DispatchQueue.main, latest: true) - .sink { [weak self] _ in + .sink { [weak self] roomInfo in guard let self else { return } - state.roomTitle = roomProxy.roomTitle - state.roomAvatar = roomProxy.avatar - state.hasOngoingCall = roomProxy.hasOngoingCall + state.roomTitle = roomInfo.displayName ?? roomProxy.id + state.roomAvatar = roomInfo.avatar + state.hasOngoingCall = roomInfo.hasRoomCall } .store(in: &cancellables) Task { [weak self] in - for await _ in roomInfoSubscription.receive(on: DispatchQueue.main).values { + for await roomInfo in roomInfoSubscription.receive(on: DispatchQueue.main).values { guard !Task.isCancelled else { return } - await self?.handleRoomInfoUpdate() + await self?.handleRoomInfoUpdate(roomInfo) } } .store(in: &cancellables) @@ -230,8 +229,8 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } } - private func handleRoomInfoUpdate() async { - let pinnedEventIDs = await roomProxy.pinnedEventIDs + private func handleRoomInfoUpdate(_ roomInfo: RoomInfoProxy) async { + let pinnedEventIDs = roomInfo.pinnedEventIDs // Only update the loading state of the banner if state.pinnedEventsBannerState.isLoading { state.pinnedEventsBannerState = .loading(numbersOfEvents: pinnedEventIDs.count) diff --git a/ElementX/Sources/Screens/Settings/NotificationSettingsEditScreen/NotificationSettingsEditScreenViewModel.swift b/ElementX/Sources/Screens/Settings/NotificationSettingsEditScreen/NotificationSettingsEditScreenViewModel.swift index c47f71a2d6..934b383798 100644 --- a/ElementX/Sources/Screens/Settings/NotificationSettingsEditScreen/NotificationSettingsEditScreenViewModel.swift +++ b/ElementX/Sources/Screens/Settings/NotificationSettingsEditScreen/NotificationSettingsEditScreenViewModel.swift @@ -131,7 +131,7 @@ class NotificationSettingsEditScreenViewModel: NotificationSettingsEditScreenVie for roomSummary in filteredRoomsSummary { guard case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(roomSummary.id) else { continue } // `isOneToOneRoom` here is not the same as `isDirect` on the room. From the point of view of the push rule, a one-to-one room is a room with exactly two active members. - let isOneToOneRoom = roomProxy.activeMembersCount == 2 + let isOneToOneRoom = roomProxy.infoPublisher.value.activeMembersCount == 2 // display only the rooms we're interested in switch chatType { case .oneToOneChat where isOneToOneRoom, .groupChat where !isOneToOneRoom: diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index 56307073d6..7a7103499d 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -82,6 +82,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { ownUserID: roomProxy.ownUserID, isViewSourceEnabled: appSettings.viewSourceEnabled, hideTimelineMedia: appSettings.hideTimelineMedia, + pinnedEventIDs: roomProxy.infoPublisher.value.pinnedEventIDs, bindings: .init(reactionsCollapsed: [:]), emojiProvider: emojiProvider), mediaProvider: mediaProvider) @@ -91,10 +92,6 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { showFocusLoadingIndicator() } - Task { - await updatePinnedEventIDs() - } - setupSubscriptions() setupDirectRoomSubscriptionsIfNeeded() @@ -380,15 +377,13 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { } .store(in: &cancellables) - let roomInfoSubscription = roomProxy - .actionsPublisher - .filter { $0 == .roomInfoUpdate } + let roomInfoSubscription = roomProxy.infoPublisher Task { [weak self] in - for await _ in roomInfoSubscription.receive(on: DispatchQueue.main).values { + for await roomInfo in roomInfoSubscription.receive(on: DispatchQueue.main).values { guard !Task.isCancelled else { return } - await self?.updatePinnedEventIDs() + self?.state.pinnedEventIDs = roomInfo.pinnedEventIDs await self?.updatePermissions() } } @@ -456,13 +451,9 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { .weakAssign(to: \.state.hideTimelineMedia, on: self) .store(in: &cancellables) } - - private func updatePinnedEventIDs() async { - state.pinnedEventIDs = await roomProxy.pinnedEventIDs - } private func setupDirectRoomSubscriptionsIfNeeded() { - guard roomProxy.isDirect else { + guard roomProxy.infoPublisher.value.isDirect else { return } @@ -471,7 +462,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { .map { [weak self] isFocused in guard let self else { return false } - return isFocused && self.roomProxy.isUserAloneInDirectRoom + return isFocused && self.roomProxy.infoPublisher.value.isUserAloneInDirectRoom } // We want to show the alert just once, so we are taking the first "true" emitted .first { $0 } @@ -742,7 +733,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { private let inviteLoadingIndicatorID = UUID().uuidString private func inviteOtherDMUserBack() { - guard roomProxy.isUserAloneInDirectRoom else { + guard roomProxy.infoPublisher.value.isUserAloneInDirectRoom else { userIndicatorController.alertInfo = .init(id: .init(), title: L10n.commonError) return } @@ -848,7 +839,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { } } -private extension RoomProxyProtocol { +private extension RoomInfoProxy { /// Checks if the other person left the room in a direct chat var isUserAloneInDirectRoom: Bool { isDirect && activeMembersCount == 1 diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 0bba67ad5e..28ed38ee12 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -696,7 +696,7 @@ class ClientProxy: ClientProxyProtocol { for roomID in roomIdentifiers { guard case let .joined(roomProxy) = await roomForIdentifier(roomID), - roomProxy.isDirect, + roomProxy.infoPublisher.value.isDirect, let members = await roomProxy.members() else { continue } @@ -869,15 +869,15 @@ class ClientProxy: ClientProxyProtocol { switch roomListItem.membership() { case .invited: - return try .invited(InvitedRoomProxy(roomListItem: roomListItem, - room: roomListItem.invitedRoom())) + return try await .invited(InvitedRoomProxy(roomListItem: roomListItem, + room: roomListItem.invitedRoom())) case .knocked: if appSettings.knockingEnabled { - return try .knocked(KnockedRoomProxy(roomListItem: roomListItem, - room: roomListItem.invitedRoom())) + return try await .knocked(KnockedRoomProxy(roomListItem: roomListItem, + room: roomListItem.invitedRoom())) } else { - return try .invited(InvitedRoomProxy(roomListItem: roomListItem, - room: roomListItem.invitedRoom())) + return try await .invited(InvitedRoomProxy(roomListItem: roomListItem, + room: roomListItem.invitedRoom())) } case .joined: if roomListItem.isTimelineInitialized() == false { diff --git a/ElementX/Sources/Services/ElementCall/ElementCallService.swift b/ElementX/Sources/Services/ElementCall/ElementCallService.swift index f08a4e712a..1a02da9826 100644 --- a/ElementX/Sources/Services/ElementCall/ElementCallService.swift +++ b/ElementX/Sources/Services/ElementCall/ElementCallService.swift @@ -291,16 +291,11 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe // it from what we have. If the call is running before subscribing then wait // for it to change to `false` otherwise wait for it to turn `true` before // changing to `false` - let isCallOngoing = roomProxy.hasOngoingCall + let isCallOngoing = roomProxy.infoPublisher.value.hasRoomCall roomProxy - .actionsPublisher - .compactMap { action -> (Bool, [String])? in - switch action { - case .roomInfoUpdate: - return (roomProxy.hasOngoingCall, roomProxy.activeRoomCallParticipants) - } - } + .infoPublisher + .compactMap { ($0.hasRoomCall, $0.activeRoomCallParticipants) } .removeDuplicates { $0 == $1 } .dropFirst(isCallOngoing ? 0 : 1) .sink { [weak self] hasOngoingCall, activeRoomCallParticipants in diff --git a/ElementX/Sources/Services/Room/InvitedRoomProxy.swift b/ElementX/Sources/Services/Room/InvitedRoomProxy.swift index 291bfa3304..4e2be76cc0 100644 --- a/ElementX/Sources/Services/Room/InvitedRoomProxy.swift +++ b/ElementX/Sources/Services/Room/InvitedRoomProxy.swift @@ -17,68 +17,15 @@ class InvitedRoomProxy: InvitedRoomProxyProtocol { // multiple times over FFI lazy var id: String = room.id() - var canonicalAlias: String? { - room.canonicalAlias() - } - - var ownUserID: String { - room.ownUserId() - } - - var name: String? { - roomListItem.displayName() - } - - var topic: String? { - room.topic() - } + var ownUserID: String { room.ownUserId() } - var avatarURL: URL? { - roomListItem.avatarUrl().flatMap(URL.init(string:)) - } - - var avatar: RoomAvatar { - if isDirect, avatarURL == nil { - let heroes = room.heroes() - - if heroes.count == 1 { - return .heroes(heroes.map(UserProfileProxy.init)) - } - } - - return .room(id: id, name: name, avatarURL: avatarURL) - } - - var isDirect: Bool { - room.isDirect() - } - - var isPublic: Bool { - room.isPublic() - } - - var isSpace: Bool { - room.isSpace() - } - - var joinedMembersCount: Int { - Int(room.joinedMembersCount()) - } - - var activeMembersCount: Int { - Int(room.activeMembersCount()) - } - - var inviter: RoomMemberProxyProtocol? { - get async { - await (try? roomListItem.roomInfo().inviter).map(RoomMemberProxy.init) - } - } + let info: RoomInfoProxy init(roomListItem: RoomListItemProtocol, - room: RoomProtocol) { + room: RoomProtocol) async throws { self.roomListItem = roomListItem self.room = room + info = try await RoomInfoProxy(roomInfo: room.roomInfo()) } func acceptInvitation() async -> Result { diff --git a/ElementX/Sources/Services/Room/JoinedRoomProxy.swift b/ElementX/Sources/Services/Room/JoinedRoomProxy.swift index 4ada990bcf..30211cc367 100644 --- a/ElementX/Sources/Services/Room/JoinedRoomProxy.swift +++ b/ElementX/Sources/Services/Room/JoinedRoomProxy.swift @@ -62,6 +62,11 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol { private var identityStatusChangesObservationToken: TaskHandle? private var subscribedForUpdates = false + + private let infoSubject: CurrentValueSubject + var infoPublisher: CurrentValuePublisher { + infoSubject.asCurrentValuePublisher() + } private let membersSubject = CurrentValueSubject<[RoomMemberProxyProtocol], Never>([]) var membersPublisher: CurrentValuePublisher<[RoomMemberProxyProtocol], Never> { @@ -77,95 +82,17 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol { var identityStatusChangesPublisher: CurrentValuePublisher<[IdentityStatusChange], Never> { identityStatusChangesSubject.asCurrentValuePublisher() } - - private let actionsSubject = PassthroughSubject() - var actionsPublisher: AnyPublisher { - actionsSubject.eraseToAnyPublisher() - } // A room identifier is constant and lazy stops it from being fetched // multiple times over FFI lazy var id: String = room.id() - - var canonicalAlias: String? { - room.canonicalAlias() - } - - var ownUserID: String { - room.ownUserId() - } - - var name: String? { - roomListItem.displayName() - } - - var topic: String? { - room.topic() - } - - var avatarURL: URL? { - roomListItem.avatarUrl().flatMap(URL.init(string:)) - } - - var avatar: RoomAvatar { - if isDirect, avatarURL == nil { - let heroes = room.heroes() - - if heroes.count == 1 { - return .heroes(heroes.map(UserProfileProxy.init)) - } - } - - return .room(id: id, name: name, avatarURL: avatarURL) - } - - var isDirect: Bool { - room.isDirect() - } - - var isPublic: Bool { - room.isPublic() - } - - var isSpace: Bool { - room.isSpace() - } - - var joinedMembersCount: Int { - Int(room.joinedMembersCount()) - } - - var activeMembersCount: Int { - Int(room.activeMembersCount()) - } + var ownUserID: String { room.ownUserId() } + var info: RoomInfoProxy { infoSubject.value } var isEncrypted: Bool { (try? room.isEncrypted()) ?? false } - var isFavourite: Bool { - get async { - await (try? room.roomInfo().isFavourite) ?? false - } - } - - var pinnedEventIDs: Set { - get async { - guard let pinnedEventIDs = try? await room.roomInfo().pinnedEventIds else { - return [] - } - return .init(pinnedEventIDs) - } - } - - var hasOngoingCall: Bool { - room.hasActiveRoomCall() - } - - var activeRoomCallParticipants: [String] { - room.activeRoomCallParticipants() - } - init(roomListService: RoomListServiceProtocol, roomListItem: RoomListItemProtocol, room: RoomProtocol) async throws { @@ -173,6 +100,7 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol { self.roomListItem = roomListItem self.room = room + infoSubject = try await .init(RoomInfoProxy(roomInfo: room.roomInfo())) timeline = try await TimelineProxy(timeline: room.timeline(), kind: .live) Task { @@ -210,9 +138,9 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol { return } - roomInfoObservationToken = room.subscribeToRoomInfoUpdates(listener: RoomInfoUpdateListener { [weak self] in + roomInfoObservationToken = room.subscribeToRoomInfoUpdates(listener: RoomInfoUpdateListener { [weak self] roomInfo in MXLog.info("Received room info update") - self?.actionsSubject.send(.roomInfoUpdate) + self?.infoSubject.send(.init(roomInfo: roomInfo)) }) } @@ -730,14 +658,14 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol { } private final class RoomInfoUpdateListener: RoomInfoListener { - private let onUpdateClosure: () -> Void + private let onUpdateClosure: (RoomInfo) -> Void - init(_ onUpdateClosure: @escaping () -> Void) { + init(_ onUpdateClosure: @escaping (RoomInfo) -> Void) { self.onUpdateClosure = onUpdateClosure } func call(roomInfo: RoomInfo) { - onUpdateClosure() + onUpdateClosure(roomInfo) } } diff --git a/ElementX/Sources/Services/Room/KnockedRoomProxy.swift b/ElementX/Sources/Services/Room/KnockedRoomProxy.swift index 7ca349f331..74b640dc54 100644 --- a/ElementX/Sources/Services/Room/KnockedRoomProxy.swift +++ b/ElementX/Sources/Services/Room/KnockedRoomProxy.swift @@ -17,62 +17,15 @@ class KnockedRoomProxy: KnockedRoomProxyProtocol { // multiple times over FFI lazy var id: String = room.id() - var canonicalAlias: String? { - room.canonicalAlias() - } - - var ownUserID: String { - room.ownUserId() - } - - var name: String? { - roomListItem.displayName() - } - - var topic: String? { - room.topic() - } - - var avatarURL: URL? { - roomListItem.avatarUrl().flatMap(URL.init(string:)) - } - - var avatar: RoomAvatar { - if isDirect, avatarURL == nil { - let heroes = room.heroes() - - if heroes.count == 1 { - return .heroes(heroes.map(UserProfileProxy.init)) - } - } - - return .room(id: id, name: name, avatarURL: avatarURL) - } - - var isDirect: Bool { - room.isDirect() - } - - var isPublic: Bool { - room.isPublic() - } + var ownUserID: String { room.ownUserId() } - var isSpace: Bool { - room.isSpace() - } - - var joinedMembersCount: Int { - Int(room.joinedMembersCount()) - } - - var activeMembersCount: Int { - Int(room.activeMembersCount()) - } + let info: RoomInfoProxy init(roomListItem: RoomListItemProtocol, - room: RoomProtocol) { + room: RoomProtocol) async throws { self.roomListItem = roomListItem self.room = room + info = try await RoomInfoProxy(roomInfo: room.roomInfo()) } func cancelKnock() async -> Result { diff --git a/ElementX/Sources/Services/Room/RoomInfoProxy.swift b/ElementX/Sources/Services/Room/RoomInfoProxy.swift new file mode 100644 index 0000000000..e402031310 --- /dev/null +++ b/ElementX/Sources/Services/Room/RoomInfoProxy.swift @@ -0,0 +1,56 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Foundation +import MatrixRustSDK + +struct RoomInfoProxy { + let roomInfo: RoomInfo + + var id: String { roomInfo.id } + var creator: String? { roomInfo.creator } + var displayName: String? { roomInfo.displayName } + var rawName: String? { roomInfo.rawName } + var topic: String? { roomInfo.topic } + /// The room's avatar URL. Use this for editing and favour ``avatar`` for display. + var avatarURL: URL? { roomInfo.avatarUrl.flatMap(URL.init) } + /// The room's avatar info for use in a ``RoomAvatarImage``. + var avatar: RoomAvatar { + if isDirect, avatarURL == nil { + if heroes.count == 1 { + return .heroes(heroes.map(UserProfileProxy.init)) + } + } + + return .room(id: id, name: displayName, avatarURL: avatarURL) + } + + var isDirect: Bool { roomInfo.isDirect } + var isPublic: Bool { roomInfo.isPublic } + var isSpace: Bool { roomInfo.isSpace } + var isTombstoned: Bool { roomInfo.isTombstoned } + var isFavourite: Bool { roomInfo.isFavourite } + var canonicalAlias: String? { roomInfo.canonicalAlias } + var alternativeAliases: [String] { roomInfo.alternativeAliases } + var membership: Membership { roomInfo.membership } + var inviter: RoomMemberProxy? { roomInfo.inviter.map(RoomMemberProxy.init) } + var heroes: [RoomHero] { roomInfo.heroes } + var activeMembersCount: Int { Int(roomInfo.activeMembersCount) } + var invitedMembersCount: Int { Int(roomInfo.invitedMembersCount) } + var joinedMembersCount: Int { Int(roomInfo.joinedMembersCount) } + var userPowerLevels: [String: Int] { roomInfo.userPowerLevels.mapValues(Int.init) } + var highlightCount: Int { Int(roomInfo.highlightCount) } + var notificationCount: Int { Int(roomInfo.notificationCount) } + var cachedUserDefinedNotificationMode: RoomNotificationMode? { roomInfo.cachedUserDefinedNotificationMode } + var hasRoomCall: Bool { roomInfo.hasRoomCall } + var activeRoomCallParticipants: [String] { roomInfo.activeRoomCallParticipants } + var isMarkedUnread: Bool { roomInfo.isMarkedUnread } + var unreadMessagesCount: UInt { UInt(roomInfo.numUnreadMessages) } + var unreadNotificationsCount: UInt { UInt(roomInfo.numUnreadNotifications) } + var unreadMentionsCount: UInt { UInt(roomInfo.numUnreadMentions) } + var pinnedEventIDs: Set { Set(roomInfo.pinnedEventIds) } +} diff --git a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift index 46cc11f080..cf9c01fd8e 100644 --- a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift @@ -28,37 +28,19 @@ enum RoomProxyType { // sourcery: AutoMockable protocol RoomProxyProtocol { var id: String { get } - var canonicalAlias: String? { get } - var ownUserID: String { get } - - var name: String? { get } - var topic: String? { get } - - /// The room's avatar info for use in a ``RoomAvatarImage``. - var avatar: RoomAvatar { get } - /// The room's avatar URL. Use this for editing and favour ``avatar`` for display. - var avatarURL: URL? { get } - - var isPublic: Bool { get } - var isDirect: Bool { get } - var isSpace: Bool { get } - - var joinedMembersCount: Int { get } - - var activeMembersCount: Int { get } } // sourcery: AutoMockable protocol InvitedRoomProxyProtocol: RoomProxyProtocol { - var inviter: RoomMemberProxyProtocol? { get async } - + var info: RoomInfoProxy { get } func rejectInvitation() async -> Result func acceptInvitation() async -> Result } // sourcery: AutoMockable protocol KnockedRoomProxyProtocol: RoomProxyProtocol { + var info: RoomInfoProxy { get } func cancelKnock() async -> Result } @@ -69,11 +51,8 @@ enum JoinedRoomProxyAction: Equatable { // sourcery: AutoMockable protocol JoinedRoomProxyProtocol: RoomProxyProtocol { var isEncrypted: Bool { get } - var isFavourite: Bool { get async } - var pinnedEventIDs: Set { get async } - var hasOngoingCall: Bool { get } - var activeRoomCallParticipants: [String] { get } + var infoPublisher: CurrentValuePublisher { get } var membersPublisher: CurrentValuePublisher<[RoomMemberProxyProtocol], Never> { get } @@ -81,8 +60,6 @@ protocol JoinedRoomProxyProtocol: RoomProxyProtocol { var identityStatusChangesPublisher: CurrentValuePublisher<[IdentityStatusChange], Never> { get } - var actionsPublisher: AnyPublisher { get } - var timeline: TimelineProxyProtocol { get } var pinnedEventsTimeline: TimelineProxyProtocol? { get async } @@ -176,21 +153,15 @@ protocol JoinedRoomProxyProtocol: RoomProxyProtocol { extension JoinedRoomProxyProtocol { var details: RoomDetails { RoomDetails(id: id, - name: name, - avatar: avatar, - canonicalAlias: canonicalAlias, + name: infoPublisher.value.displayName, + avatar: infoPublisher.value.avatar, + canonicalAlias: infoPublisher.value.canonicalAlias, isEncrypted: isEncrypted, - isPublic: isPublic) - } - - // Avoids to duplicate the same logic around in the app - // Probably this should be done in rust. - var roomTitle: String { - name ?? "Unknown room 💥" + isPublic: infoPublisher.value.isPublic) } var isEncryptedOneToOneRoom: Bool { - isDirect && isEncrypted && activeMembersCount <= 2 + infoPublisher.value.isDirect && isEncrypted && infoPublisher.value.activeMembersCount <= 2 } func members() async -> [RoomMemberProxyProtocol]? { diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift index b7e704324c..09681aa418 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift @@ -326,7 +326,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol { switch paginationState.backward { case .timelineEndReached: if timelineKind != .pinned, !roomProxy.isEncryptedOneToOneRoom { - let timelineStart = TimelineStartRoomTimelineItem(name: roomProxy.name) + let timelineStart = TimelineStartRoomTimelineItem(name: roomProxy.infoPublisher.value.displayName) newTimelineItems.insert(timelineStart, at: 0) } case .paginating: diff --git a/UnitTests/Sources/RoomScreenViewModelTests.swift b/UnitTests/Sources/RoomScreenViewModelTests.swift index e21891bb43..773c9af2c7 100644 --- a/UnitTests/Sources/RoomScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomScreenViewModelTests.swift @@ -24,15 +24,16 @@ class RoomScreenViewModelTests: XCTestCase { } func testPinnedEventsBanner() async throws { + var configuration = JoinedRoomProxyMockConfiguration() let timelineSubject = PassthroughSubject() - let updateSubject = PassthroughSubject() - let roomProxyMock = JoinedRoomProxyMock(.init()) + let infoSubject = CurrentValueSubject(.init(roomInfo: RoomInfo(configuration))) + let roomProxyMock = JoinedRoomProxyMock(configuration) // setup a way to inject the mock of the pinned events timeline roomProxyMock.pinnedEventsTimelineClosure = { await timelineSubject.values.first() } // setup the room proxy actions publisher - roomProxyMock.underlyingActionsPublisher = updateSubject.eraseToAnyPublisher() + roomProxyMock.underlyingInfoPublisher = infoSubject.asCurrentValuePublisher() let viewModel = RoomScreenViewModel(clientProxy: ClientProxyMock(), roomProxy: roomProxyMock, initialSelectedPinnedEventID: nil, @@ -56,8 +57,8 @@ class RoomScreenViewModelTests: XCTestCase { deferred = deferFulfillment(viewModel.context.$viewState) { viewState in viewState.pinnedEventsBannerState.count == 2 } - roomProxyMock.underlyingPinnedEventIDs = ["test1", "test2"] - updateSubject.send(.roomInfoUpdate) + configuration.pinnedEventIDs = ["test1", "test2"] + infoSubject.send(.init(roomInfo: RoomInfo(configuration))) try await deferred.fulfill() XCTAssertTrue(viewModel.context.viewState.pinnedEventsBannerState.isLoading) XCTAssertTrue(viewModel.context.viewState.shouldShowPinnedEventsBanner) @@ -157,11 +158,12 @@ class RoomScreenViewModelTests: XCTestCase { } func testRoomInfoUpdate() async throws { - let updateSubject = PassthroughSubject() - let roomProxyMock = JoinedRoomProxyMock(.init(id: "TestID", name: "StartingName", avatarURL: nil, hasOngoingCall: false)) + var configuration = JoinedRoomProxyMockConfiguration(id: "TestID", name: "StartingName", avatarURL: nil, hasOngoingCall: false) + let infoSubject = CurrentValueSubject(.init(roomInfo: RoomInfo(configuration))) + let roomProxyMock = JoinedRoomProxyMock(configuration) // setup the room proxy actions publisher roomProxyMock.canUserJoinCallUserIDReturnValue = .success(false) - roomProxyMock.underlyingActionsPublisher = updateSubject.eraseToAnyPublisher() + roomProxyMock.underlyingInfoPublisher = infoSubject.asCurrentValuePublisher() let viewModel = RoomScreenViewModel(clientProxy: ClientProxyMock(), roomProxy: roomProxyMock, initialSelectedPinnedEventID: nil, @@ -181,9 +183,9 @@ class RoomScreenViewModelTests: XCTestCase { } try await deferred.fulfill() - roomProxyMock.name = "NewName" - roomProxyMock.avatar = .room(id: "TestID", name: "NewName", avatarURL: .documentsDirectory) - roomProxyMock.hasOngoingCall = true + configuration.name = "NewName" + configuration.avatarURL = .documentsDirectory + configuration.hasOngoingCall = true roomProxyMock.canUserJoinCallUserIDReturnValue = .success(true) deferred = deferFulfillment(viewModel.context.$viewState) { viewState in @@ -193,7 +195,7 @@ class RoomScreenViewModelTests: XCTestCase { viewState.hasOngoingCall } - updateSubject.send(.roomInfoUpdate) + infoSubject.send(.init(roomInfo: RoomInfo(configuration))) try await deferred.fulfill() } diff --git a/UnitTests/Sources/TimelineViewModelTests.swift b/UnitTests/Sources/TimelineViewModelTests.swift index 28f2ad0d32..fb89dc6f3a 100644 --- a/UnitTests/Sources/TimelineViewModelTests.swift +++ b/UnitTests/Sources/TimelineViewModelTests.swift @@ -349,10 +349,11 @@ class TimelineViewModelTests: XCTestCase { // MARK: - Pins func testPinnedEvents() async throws { - let roomProxyMock = JoinedRoomProxyMock(.init(name: "", - pinnedEventIDs: .init(["test1"]))) - let actionsSubject = PassthroughSubject() - roomProxyMock.underlyingActionsPublisher = actionsSubject.eraseToAnyPublisher() + var configuration = JoinedRoomProxyMockConfiguration(name: "", + pinnedEventIDs: .init(["test1"])) + let roomProxyMock = JoinedRoomProxyMock(configuration) + let infoSubject = CurrentValueSubject(.init(roomInfo: RoomInfo(configuration))) + roomProxyMock.underlyingInfoPublisher = infoSubject.asCurrentValuePublisher() let viewModel = TimelineViewModel(roomProxy: roomProxyMock, timelineController: MockRoomTimelineController(), @@ -364,24 +365,21 @@ class TimelineViewModelTests: XCTestCase { appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) + XCTAssertEqual(configuration.pinnedEventIDs, viewModel.context.viewState.pinnedEventIDs) - var deferred = deferFulfillment(viewModel.context.$viewState) { value in - value.pinnedEventIDs == ["test1"] - } - try await deferred.fulfill() - - roomProxyMock.underlyingPinnedEventIDs = ["test1", "test2"] - deferred = deferFulfillment(viewModel.context.$viewState) { value in + configuration.pinnedEventIDs = ["test1", "test2"] + let deferred = deferFulfillment(viewModel.context.$viewState) { value in value.pinnedEventIDs == ["test1", "test2"] } - actionsSubject.send(.roomInfoUpdate) + infoSubject.send(.init(roomInfo: RoomInfo(configuration))) try await deferred.fulfill() } func testCanUserPinEvents() async throws { - let roomProxyMock = JoinedRoomProxyMock(.init(name: "", canUserPin: true)) - let actionsSubject = PassthroughSubject() - roomProxyMock.underlyingActionsPublisher = actionsSubject.eraseToAnyPublisher() + let configuration = JoinedRoomProxyMockConfiguration(name: "", canUserPin: true) + let roomProxyMock = JoinedRoomProxyMock(configuration) + let infoSubject = CurrentValueSubject(.init(roomInfo: RoomInfo(configuration))) + roomProxyMock.underlyingInfoPublisher = infoSubject.asCurrentValuePublisher() let viewModel = TimelineViewModel(roomProxy: roomProxyMock, timelineController: MockRoomTimelineController(), @@ -403,7 +401,7 @@ class TimelineViewModelTests: XCTestCase { deferred = deferFulfillment(viewModel.context.$viewState) { value in !value.canCurrentUserPin } - actionsSubject.send(.roomInfoUpdate) + infoSubject.send(.init(roomInfo: RoomInfo(configuration))) try await deferred.fulfill() } From a583892e019780516502c090926e77aa43097bfd Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 28 Oct 2024 14:40:02 +0000 Subject: [PATCH 088/114] Update translations and some snapshots. (#3459) * Update snapshots. * Update translations. --- .../be.lproj/Localizable.strings | 19 ++- .../bg.lproj/Localizable.strings | 25 ++- .../cs.lproj/Localizable.strings | 35 +++-- .../de.lproj/Localizable.strings | 19 ++- .../el.lproj/Localizable.strings | 47 ++++-- .../en.lproj/Localizable.strings | 2 + .../es.lproj/Localizable.strings | 19 ++- .../et.lproj/Localizable.strings | 37 +++-- .../fa.lproj/Localizable.strings | 19 ++- .../fr.lproj/Localizable.strings | 19 ++- .../hu.lproj/Localizable.strings | 19 ++- .../id.lproj/Localizable.strings | 145 ++++++++++-------- .../id.lproj/Localizable.stringsdict | 4 +- .../it.lproj/Localizable.strings | 19 ++- .../ka.lproj/Localizable.strings | 19 ++- .../nl.lproj/Localizable.strings | 19 ++- .../pl.lproj/Localizable.strings | 19 ++- .../pt-BR.lproj/Localizable.strings | 19 ++- .../pt.lproj/Localizable.strings | 19 ++- .../ro.lproj/Localizable.strings | 19 ++- .../ru.lproj/Localizable.strings | 123 ++++++++------- .../sk.lproj/Localizable.strings | 83 ++++++---- .../sv.lproj/Localizable.strings | 33 +++- .../uk.lproj/Localizable.strings | 19 ++- .../uz.lproj/Localizable.strings | 19 ++- .../zh-Hans.lproj/Localizable.strings | 19 ++- .../zh-Hant-TW.lproj/Localizable.strings | 25 ++- ElementX/Sources/Generated/Strings.swift | 4 + ...eateRoom-iPad-en-GB.Create-Public-Room.png | 4 +- ...m-iPad-en-GB.Create-Room-without-users.png | 4 +- ...test_createRoom-iPad-en-GB.Create-Room.png | 4 +- ...ateRoom-iPad-pseudo.Create-Public-Room.png | 4 +- ...-iPad-pseudo.Create-Room-without-users.png | 4 +- ...est_createRoom-iPad-pseudo.Create-Room.png | 4 +- ...oom-iPhone-16-en-GB.Create-Public-Room.png | 4 +- ...one-16-en-GB.Create-Room-without-users.png | 4 +- ...createRoom-iPhone-16-en-GB.Create-Room.png | 4 +- ...om-iPhone-16-pseudo.Create-Public-Room.png | 4 +- ...ne-16-pseudo.Create-Room-without-users.png | 4 +- ...reateRoom-iPhone-16-pseudo.Create-Room.png | 4 +- 40 files changed, 672 insertions(+), 243 deletions(-) diff --git a/ElementX/Resources/Localizations/be.lproj/Localizable.strings b/ElementX/Resources/Localizations/be.lproj/Localizable.strings index deaf7b6ca1..b32142aa8e 100644 --- a/ElementX/Resources/Localizations/be.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/be.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(Адрэдагавана)"; "common_editing" = "Рэдагаванне"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "Шыфраванне ўключана"; "common_enter_your_pin" = "Увядзіце свой PIN-код"; "common_error" = "Памылка"; @@ -149,6 +150,7 @@ "common_favourited" = "Абранае"; "common_file" = "Файл"; "common_forward_message" = "Перасылка паведамлення"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Відарыс"; "common_in_reply_to" = "У адказ на %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Замацаваны"; "common.send_to" = "Адправіць"; "common.you" = "Вы"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Ваша рэзервовая копія чата зараз не сінхранізавана. Вам трэба пацвердзіць ключ аднаўлення, каб захаваць доступ да рэзервовай копіі чата."; "confirm_recovery_key_banner_title" = "Увядзіце ключ аднаўлення"; "crash_detection_dialog_content" = "Пры апошнім выкарыстанні %1$@ адбыўся збой. Хочаце падзяліцца справаздачай аб збоі?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "Карыстальніцкі URL сервера Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Усталюйце карыстальніцкі асноўны URL для Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Адрас пазначаны няправільна, пераканайцеся, што вы ўказалі пратакол (http/https) і правільны адрас."; +"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" = "Хто заўгодна"; "screen_create_room_access_section_header" = "Доступ у пакой"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Загрузка паведамлення…"; "screen_room_pinned_banner_view_all_button_title" = "Паглядзець усе"; "screen_room_details_pinned_events_row_title" = "Замацаваныя паведамленні"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Вы збіраецеся стварыць уліковы запіс на %@"; "screen_advanced_settings_developer_mode" = "Рэжым распрацоўшчыка"; "screen_advanced_settings_developer_mode_description" = "Падайце распрацоўнікам доступ да функцый і функцыянальным магчымасцям."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Адключыць рэдактар фарматаванага тэксту і ўключыць Markdown."; "screen_advanced_settings_send_read_receipts" = "Апавяшчэнні аб чытанні"; "screen_advanced_settings_send_read_receipts_description" = "Калі выключыць, вашы пасведчанні аб прачытанні нікому не будуць адпраўляцца. Вы па-ранейшаму будзеце атрымліваць пасведчанні аб прачытанні ад іншых карыстальнікаў."; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "Уключыце рэзервовае капіраванне"; "screen_chat_backup_key_backup_description" = "Рэзервовае капіраванне гарантуе, што вы не страціце сваю гісторыю паведамленняў. %1$@."; "screen_chat_backup_key_backup_title" = "Рэзервовая копія"; +"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" = "Змяніць ключ аднаўлення"; +"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" = "Ваша рэзервовая копія чата зараз не сінхранізавана."; "screen_chat_backup_recovery_action_setup" = "Наладзьце аднаўленне"; "screen_chat_backup_recovery_action_setup_description" = "Атрымайце доступ да зашыфраваных паведамленняў, калі вы страціце ўсе свае прылады або выйдзеце з сістэмы %1$@ усюды."; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Ваш код спраўджання"; "screen_recovery_key_change_description" = "Атрымайце новы ключ аднаўлення, калі вы страцілі існуючы. Пасля змены ключа аднаўлення ваш стары больш не будзе працаваць."; "screen_recovery_key_change_generate_key" = "Стварыць новы ключ аднаўлення"; -"screen_recovery_key_change_generate_key_description" = "Пераканайцеся, што вы можаце захаваць ключ аднаўлення ў бяспечным месцы"; "screen_recovery_key_change_success" = "Ключ аднаўлення зменены"; "screen_recovery_key_change_title" = "Змяніць ключ аднаўлення?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Стварыць новы ключ аднаўлення"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Параўнайце ўнікальны набор эмодзі."; "screen_session_verification_request_accepted_subtitle" = "Параўнайце ўнікальныя эмодзі, пераканаўшыся, што яны размешчаны ў тым жа парадку."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Яны не супадаюць"; "screen_session_verification_they_match" = "Яны супадаюць"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix - гэта адкрытая сетка для бяспечнай, дэцэнтралізаванай сувязі."; "screen_notification_settings_mentions_section_title" = "Згадванні"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Паўтарыць спробу"; +"screen_recovery_key_change_generate_key_description" = "Пераканайцеся, што вы можаце захаваць ключ аднаўлення ў бяспечным месцы"; "screen_recovery_key_confirm_title" = "Увядзіце ключ аднаўлення"; "screen_report_content_block_user" = "Заблакіраваць карыстальніка"; "screen_reset_encryption_password_placeholder" = "Увесці..."; diff --git a/ElementX/Resources/Localizations/bg.lproj/Localizable.strings b/ElementX/Resources/Localizations/bg.lproj/Localizable.strings index 35b3aa9a49..ea1cf7fad3 100644 --- a/ElementX/Resources/Localizations/bg.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/bg.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(редактирано)"; "common_editing" = "Редактиране"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "Шифроването е включено"; "common_enter_your_pin" = "Въведете своя PIN"; "common_error" = "Грешка"; @@ -149,6 +150,7 @@ "common_favourited" = "Favourited"; "common_file" = "Файл"; "common_forward_message" = "Препращане на съобщението"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Изображение"; "common_in_reply_to" = "В отговор на %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Pinned"; "common.send_to" = "Send to"; "common.you" = "You"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Резервното копие на чатовете ви в момента не е синхронизирано. Въведете ключа си за възстановяване, за да потвърдите достъпа до резервното копие на чатовете си."; "confirm_recovery_key_banner_title" = "Потвърдете ключа си за възстановяване"; "crash_detection_dialog_content" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; @@ -340,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"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Loading message…"; "screen_room_pinned_banner_view_all_button_title" = "View All"; "screen_room_details_pinned_events_row_title" = "Pinned messages"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "На път сте да създадете акаунт в %@"; "screen_advanced_settings_developer_mode" = "Developer mode"; "screen_advanced_settings_developer_mode_description" = "Enable to have access to features and functionality for developers."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Disable the rich text editor to type Markdown manually."; "screen_advanced_settings_send_read_receipts" = "Потвърждения за прочитане"; "screen_advanced_settings_send_read_receipts_description" = "If turned off, your read receipts won't be sent to anyone. You will still receive read receipts from other users."; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "Включване на резервните копия"; "screen_chat_backup_key_backup_description" = "Резервното копие гарантира, че няма да загубите хронологията на съобщенията си. %1$@."; "screen_chat_backup_key_backup_title" = "Резервно копие"; +"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" = "Промяна на ключа за възстановяване"; +"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" = "Резервното копие на чатовете ви в момента не е синхронизирано."; "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."; @@ -621,7 +634,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" = "Генериране на нов ключ за възстановяване"; -"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" = "Промяна на ключа за възстановяване?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Create new recovery key"; @@ -635,14 +647,14 @@ "screen_recovery_key_copied_to_clipboard" = "Копиран ключ за възстановяване"; "screen_recovery_key_generating_key" = "Generating…"; "screen_recovery_key_save_action" = "Запазване на ключа за възстановяване"; -"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"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Сравнете уникален набор от емоджита."; "screen_session_verification_request_accepted_subtitle" = "Compare the unique emoji, ensuring they appear in the same order."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Те не съвпадат"; "screen_session_verification_they_match" = "Те съвпадат"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix е отворена мрежа за сигурна, децентрализирана комуникация."; "screen_notification_settings_mentions_section_title" = "Споменавания"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Повторен опит"; +"screen_recovery_key_change_generate_key_description" = "Do not share this with anyone!"; "screen_recovery_key_confirm_title" = "Потвърдете ключа си за възстановяване"; "screen_report_content_block_user" = "Блокиране на потребителя"; "screen_reset_encryption_password_placeholder" = "Въведете…"; diff --git a/ElementX/Resources/Localizations/cs.lproj/Localizable.strings b/ElementX/Resources/Localizations/cs.lproj/Localizable.strings index f6b8bd68a8..b05c85c3f3 100644 --- a/ElementX/Resources/Localizations/cs.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/cs.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(upraveno)"; "common_editing" = "Úpravy"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Šifrování"; "common_encryption_enabled" = "Šifrování povoleno"; "common_enter_your_pin" = "Zadejte svůj PIN"; "common_error" = "Chyba"; @@ -149,6 +150,7 @@ "common_favourited" = "Oblíbené"; "common_file" = "Soubor"; "common_forward_message" = "Přeposlat zprávu"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Obrázek"; "common_in_reply_to" = "V odpovědi na %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Připnuto"; "common.send_to" = "Odeslat do"; "common.you" = "Vy"; +"common_unable_to_decrypt_insecure_device" = "Šifrováno nezabezpečeným zařízením"; +"common_unable_to_decrypt_verification_violation" = "Ověřená identita odesílatele se změnila"; "confirm_recovery_key_banner_message" = "Vaše záloha chatu není aktuálně synchronizována. Abyste si zachovali přístup k záloze chatu, musíte potvrdit klíč pro obnovení."; "confirm_recovery_key_banner_title" = "Potvrďte klíč pro obnovení"; "crash_detection_dialog_content" = "%1$@ havaroval při posledním použití. Chcete se s námi podělit o zprávu o selhání?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "Vlastní URL pro Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Nastavte vlastní URL pro Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Neplatné URL, ujistěte se, že jste uvedli protokol (http/https) a správnou adresu."; +"screen_create_room_room_address_section_footer" = "Aby byla tato místnost viditelná v adresáři veřejných místností, budete potřebovat adresu místnosti."; +"screen_create_room_room_address_section_title" = "Adresa místnosti"; +"screen_create_room_room_visibility_section_title" = "Viditelnost místnosti"; "screen_create_room_access_section_anyone_option_description" = "Do této místnosti může vstoupit kdokoli"; "screen_create_room_access_section_anyone_option_title" = "Kdokoliv"; "screen_create_room_access_section_header" = "Přístup do místnosti"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Načítání zprávy..."; "screen_room_pinned_banner_view_all_button_title" = "Zobrazit vše"; "screen_room_details_pinned_events_row_title" = "Připnuté zprávy"; +"screen_roomlist_knock_event_sent_description" = "Žádost o vstup odeslána"; "screen_timeline_item_menu_send_failure_changed_identity" = "Zpráva nebyla odeslána, protože ověřená identita uživatele %1$@ se změnila."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Zpráva nebyla odeslána, protože%1$@ neověřil(a) všechna zařízení."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Zpráva nebyla odeslána, protože jste neověřili jedno nebo více zařízení."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Chystáte se vytvořit účet na %@"; "screen_advanced_settings_developer_mode" = "Vývojářský režim"; "screen_advanced_settings_developer_mode_description" = "Povolením získáte přístup k funkcím a funkcím pro vývojáře."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Vypněte editor formátovaného textu pro ruční zadání Markdown."; "screen_advanced_settings_send_read_receipts" = "Potvrzení o přečtení"; "screen_advanced_settings_send_read_receipts_description" = "Pokud je vypnuto, potvrzení o přečtení se nikomu neodesílají. Stále budete dostávat potvrzení o přečtení od ostatních uživatelů."; @@ -446,9 +456,12 @@ "screen_change_server_title" = "Vyberte váš server"; "screen_chat_backup_key_backup_action_disable" = "Vypnout zálohování"; "screen_chat_backup_key_backup_action_enable" = "Zapnout zálohování"; -"screen_chat_backup_key_backup_description" = "Zálohování zajistí, že neztratíte historii zpráv. %1$@."; -"screen_chat_backup_key_backup_title" = "Záloha"; +"screen_chat_backup_key_backup_description" = "Bezpečně uložte svou kryptografickou identitu a klíče zpráv na serveru. To vám umožní zobrazit historii zpráv na všech nových zařízeních. %1$@."; +"screen_chat_backup_key_backup_title" = "Úložiště klíčů"; +"screen_chat_backup_key_storage_toggle_description" = "Nahrát klíče z tohoto zařízení"; +"screen_chat_backup_key_storage_toggle_title" = "Povolit ukládání klíčů"; "screen_chat_backup_recovery_action_change" = "Změnit klíč pro obnovení"; +"screen_chat_backup_recovery_action_change_description" = "Obnovte svou kryptografickou identitu a historii zpráv pomocí klíče pro obnovení, pokud jste ztratili všechna stávající zařízení."; "screen_chat_backup_recovery_action_confirm_description" = "Vaše záloha chatu není aktuálně synchronizována."; "screen_chat_backup_recovery_action_setup" = "Nastavení obnovy"; "screen_chat_backup_recovery_action_setup_description" = "Získejte přístup ke svým zašifrovaným zprávám, pokud ztratíte všechna zařízení nebo jste všude odhlášeni z %1$@."; @@ -470,10 +483,10 @@ "screen_create_poll_title" = "Vytvořit hlasování"; "screen_create_room_action_create_room" = "Nová místnost"; "screen_create_room_error_creating_room" = "Při vytváření místnosti došlo k chybě"; -"screen_create_room_private_option_description" = "Zprávy v této místnosti jsou šifrované. Šifrování nelze později vypnout."; -"screen_create_room_private_option_title" = "Soukromá místnost (jen pro pozvané)"; -"screen_create_room_public_option_description" = "Zprávy nejsou šifrované a může si je přečíst kdokoli. Šifrování můžete povolit později."; -"screen_create_room_public_option_title" = "Veřejná místnost (kdokoli)"; +"screen_create_room_private_option_description" = "Do této místnosti mají přístup pouze pozvaní lidé. Všechny zprávy jsou koncově šifrovány."; +"screen_create_room_private_option_title" = "Soukromá místnost"; +"screen_create_room_public_option_description" = "Tuto místnost může najít kdokoli.\nTo můžete kdykoli změnit v nastavení místnosti."; +"screen_create_room_public_option_title" = "Veřejná místnost"; "screen_create_room_topic_label" = "Téma (nepovinné)"; "screen_deactivate_account_confirmation_dialog_content" = "Potvrďte prosím, že chcete svůj účet deaktivovat. Tuto akci nelze vrátit zpět."; "screen_deactivate_account_delete_all_messages" = "Smazat všechny mé zprávy"; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Váš ověřovací kód"; "screen_recovery_key_change_description" = "Získejte nový klíč pro obnovení, pokud jste ztratili stávající klíč. Po změně klíče pro obnovení již váš starý klíč nebude fungovat."; "screen_recovery_key_change_generate_key" = "Vygenerovat nový klíč pro obnovení"; -"screen_recovery_key_change_generate_key_description" = "Ujistěte se, že můžete klíč pro obnovení uložit někde v bezpečí"; "screen_recovery_key_change_success" = "Klíč pro obnovení byl změněn"; "screen_recovery_key_change_title" = "Změnit klíč pro obnovení?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Vytvořit nový klíč pro obnovení"; @@ -635,14 +647,14 @@ "screen_recovery_key_copied_to_clipboard" = "Klíč pro obnovení zkopírován"; "screen_recovery_key_generating_key" = "Generování..."; "screen_recovery_key_save_action" = "Uložit klíč pro obnovení"; -"screen_recovery_key_save_description" = "Zapište si klíč pro obnovení na bezpečné místo nebo jej uložte do správce hesel."; +"screen_recovery_key_save_description" = "Zapište si tento obnovovací klíč na bezpečné místo, jako je správce hesel, zašifrovaná poznámka nebo fyzický trezor."; "screen_recovery_key_save_key_description" = "Klepnutím zkopírujte klíč pro obnovení"; "screen_recovery_key_save_title" = "Uložte si klíč pro obnovení"; "screen_recovery_key_setup_confirmation_description" = "Po tomto kroku nebudete mít přístup k novému klíči pro obnovení."; "screen_recovery_key_setup_confirmation_title" = "Uložili jste si klíč pro obnovení?"; "screen_recovery_key_setup_description" = "Záloha chatu je chráněna klíčem pro obnovení. Pokud potřebujete nový klíč pro obnovení po nastavení, můžete jej znovu vytvořit výběrem možnosti „Změnit klíč pro obnovení“."; "screen_recovery_key_setup_generate_key" = "Vygenerovat klíč pro obnovení"; -"screen_recovery_key_setup_generate_key_description" = "Ujistěte se, že můžete klíč pro obnovení uložit někde v bezpečí"; +"screen_recovery_key_setup_generate_key_description" = "Toto s nikým nesdílejte!"; "screen_recovery_key_setup_success" = "Nastavení obnovení bylo úspěšné"; "screen_recovery_key_setup_title" = "Nastavení obnovy"; "screen_report_content_block_user_hint" = "Zaškrtněte, pokud chcete skrýt všechny aktuální a budoucí zprávy od tohoto uživatele"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Porovnejte jedinečnou sadu emotikonů."; "screen_session_verification_request_accepted_subtitle" = "Porovnejte jedinečné emotikony a ujistěte se, že jsou zobrazeny ve stejném pořadí."; "screen_session_verification_request_details_timestamp" = "Přihlášen"; +"screen_session_verification_request_failure_subtitle" = "Buď vypršel časový limit požadavku, požadavek byl zamítnut, nebo došlo k nesouladu ověření."; +"screen_session_verification_request_failure_title" = "Ověření se nezdařilo"; "screen_session_verification_request_footer" = "Pokračujte, pouze pokud jste toto ověření zahájili."; "screen_session_verification_request_subtitle" = "Ověřte druhé zařízení, aby byla vaše historie zpráv zabezpečená."; +"screen_session_verification_request_success_subtitle" = "Nyní můžete bezpečně číst nebo odesílat zprávy na svém druhém zařízení."; +"screen_session_verification_request_success_title" = "Zařízení ověřeno"; "screen_session_verification_request_title" = "Požadováno ověření"; "screen_session_verification_they_dont_match" = "Neshodují se"; "screen_session_verification_they_match" = "Shodují se"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix je otevřená síť pro bezpečnou a decentralizovanou komunikaci."; "screen_notification_settings_mentions_section_title" = "Zmínky"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Zkusit znovu"; +"screen_recovery_key_change_generate_key_description" = "Toto s nikým nesdílejte!"; "screen_recovery_key_confirm_title" = "Potvrďte klíč pro obnovení"; "screen_report_content_block_user" = "Zablokovat uživatele"; "screen_reset_encryption_password_placeholder" = "Zadejte..."; diff --git a/ElementX/Resources/Localizations/de.lproj/Localizable.strings b/ElementX/Resources/Localizations/de.lproj/Localizable.strings index e3f1d512ca..fad71f88a8 100644 --- a/ElementX/Resources/Localizations/de.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/de.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(bearbeitet)"; "common_editing" = "Bearbeitung"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "Verschlüsselung aktiviert"; "common_enter_your_pin" = "PIN eingeben"; "common_error" = "Fehler"; @@ -149,6 +150,7 @@ "common_favourited" = "Favorit"; "common_file" = "Datei"; "common_forward_message" = "Nachricht weiterleiten"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Bild"; "common_in_reply_to" = "Als Antwort auf %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Fixiert"; "common.send_to" = "Senden an"; "common.you" = "You"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Dein Chat-Backup ist derzeit nicht synchronisiert. Du musst deinen Wiederherstellungsschlüssel bestätigen, um Zugriff auf dein Chat-Backup zu erhalten."; "confirm_recovery_key_banner_title" = "Wiederherstellungsschlüssel bestätigen."; "crash_detection_dialog_content" = "%1$@ ist bei der letzten Nutzung abgestürzt. Möchtest du einen Absturzbericht mit uns teilen?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "Benutzerdefinierte Element-Aufruf-Basis-URL"; "screen_advanced_settings_element_call_base_url_description" = "Lege eine eigene Basis-URL für Element Call fest."; "screen_advanced_settings_element_call_base_url_validation_error" = "Ungültige URL, bitte stelle sicher, dass du das Protokoll (http/https) und die richtige Adresse angibst."; +"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"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Nachricht wird geladen…"; "screen_room_pinned_banner_view_all_button_title" = "Alle anzeigen"; "screen_room_details_pinned_events_row_title" = "Fixierte Nachrichten"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Nachricht nicht gesendet, weil sich die verifizierte Identität von %1$@ geändert hat."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Nachricht wurde nicht gesendet, weil %1$@ nicht alle Geräte verifiziert hat"; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Du bist dabei, ein Konto bei %@ zu erstellen"; "screen_advanced_settings_developer_mode" = "Entwickler-Modus"; "screen_advanced_settings_developer_mode_description" = "Aktivieren, um Zugriff auf Features und Funktionen für Entwickler zu aktivieren."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Deaktiviere den Rich-Text-Editor, um Markdown manuell einzugeben."; "screen_advanced_settings_send_read_receipts" = "Lesebestätigungen"; "screen_advanced_settings_send_read_receipts_description" = "Wenn diese Option deaktiviert ist, werden Ihre Lesebestätigungen an niemanden gesendet. Du erhältst weiterhin Lesebestätigungen von anderen Benutzern."; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "Backup aktivieren"; "screen_chat_backup_key_backup_description" = "Das Backup stellt sicher, dass du deinen Nachrichtenverlauf nicht verlierst. %1$@."; "screen_chat_backup_key_backup_title" = "Backup"; +"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" = "Wiederherstellungsschlüssel ändern"; +"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" = "Dein Chat-Backup ist derzeit nicht synchronisiert."; "screen_chat_backup_recovery_action_setup" = "Wiederherstellung einrichten"; "screen_chat_backup_recovery_action_setup_description" = "Erhalte Zugriff auf deine verschlüsselten Nachrichten, wenn du alle deine Geräte verlierst oder von %1$@ überall abgemeldet bist."; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Dein Verifizierungscode"; "screen_recovery_key_change_description" = "Hier kannst Du einen neuen Wiederherstellungsschlüssel erstellen. Nachdem Du einen neuen Wiederherstellungsschlüssel erstellt hast, funktioniert dein alter Schlüssel nicht mehr."; "screen_recovery_key_change_generate_key" = "Wiederherstellungsschlüssel erstellen"; -"screen_recovery_key_change_generate_key_description" = "Stelle sicher, dass du deinen Wiederherstellungsschlüssel an einem sicheren Ort aufbewahren kannst"; "screen_recovery_key_change_success" = "Wiederherstellungsschlüssel geändert"; "screen_recovery_key_change_title" = "Wiederherstellungsschlüssel ändern?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Neuen Wiederherstellungsschlüssel erstellen"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Vergleiche eine spezielle Reihe von Emojis."; "screen_session_verification_request_accepted_subtitle" = "Vergleiche die einzelnen Emojis und stelle sicher, dass sie in der gleichen Reihenfolge erscheinen."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Sie stimmen nicht überein"; "screen_session_verification_they_match" = "Sie stimmen überein"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix ist ein offenes Netzwerk für eine sichere, dezentrale Kommunikation."; "screen_notification_settings_mentions_section_title" = "Erwähnungen"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Erneut versuchen"; +"screen_recovery_key_change_generate_key_description" = "Stelle sicher, dass du deinen Wiederherstellungsschlüssel an einem sicheren Ort aufbewahren kannst"; "screen_recovery_key_confirm_title" = "Wiederherstellungsschlüssel bestätigen."; "screen_report_content_block_user" = "Benutzer blockieren"; "screen_reset_encryption_password_placeholder" = "Eingeben..."; diff --git a/ElementX/Resources/Localizations/el.lproj/Localizable.strings b/ElementX/Resources/Localizations/el.lproj/Localizable.strings index dfd8b111d2..f608c1385f 100644 --- a/ElementX/Resources/Localizations/el.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/el.lproj/Localizable.strings @@ -54,7 +54,7 @@ "action_forgot_password" = "Ξέχασες τον κωδικό πρόσβασης;"; "action_forward" = "Προώθηση"; "action_go_back" = "Πήγαινε πίσω"; -"action_ignore" = "Ignore"; +"action_ignore" = "Παράβλεψη"; "action_invite" = "Πρόσκληση"; "action_invite_friends" = "Πρόσκληση ατόμων"; "action_invite_friends_to_app" = "Πρόσκληση ατόμων στο %1$@"; @@ -134,11 +134,12 @@ "common_dark" = "Σκοτεινό"; "common_decryption_error" = "Σφάλμα αποκρυπτογράφησης"; "common_developer_options" = "Επιλογές προγραμματιστή"; -"common_device_id" = "Device ID"; +"common_device_id" = "ID συσκευής"; "common_direct_chat" = "Άμεση συνομιλία"; "common_edited_suffix" = "(επεξεργάστηκε)"; "common_editing" = "Επεξεργάζεται"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "Η κρυπτογράφηση ενεργοποιήθηκε"; "common_enter_your_pin" = "Εισήγαγε το PIN σου"; "common_error" = "Σφάλμα"; @@ -149,6 +150,7 @@ "common_favourited" = "Είναι αγαπημένο"; "common_file" = "Αρχείο"; "common_forward_message" = "Προώθηση μηνύματος"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Εικόνα"; "common_in_reply_to" = "Σε απάντηση στον χρήστη %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Καρφιτσωμένο"; "common.send_to" = "Αποστολή σε"; "common.you" = "Εσύ"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Το αντίγραφο ασφαλείας της συνομιλίας σου δεν είναι συγχρονισμένο αυτήν τη στιγμή. Πρέπει να εισαγάγεις το κλειδί ανάκτησης για να διατηρήσεις την πρόσβαση στο αντίγραφο ασφαλείας της συνομιλίας σου."; "confirm_recovery_key_banner_title" = "Εισήγαγε το κλειδί ανάκτησης"; "crash_detection_dialog_content" = "Το %1$@ διακόπηκε την τελευταία φορά που χρησιμοποιήθηκε. Θα 'θελες να μοιραστείς μια αναφορά σφάλματος μαζί μας;"; @@ -340,18 +344,21 @@ "screen_advanced_settings_element_call_base_url" = "Προσαρμοσμένο URL βάσης κλήσεων Element"; "screen_advanced_settings_element_call_base_url_description" = "Όρισε μια προσαρμοσμένη διεύθυνση βάσης URL για κλήση Element."; "screen_advanced_settings_element_call_base_url_validation_error" = "Μη έγκυρη διεύθυνση URL, βεβαιώσου ότι έχεις συμπεριλάβει το πρωτόκολλο (http/https) και τη σωστή διεύθυνση."; +"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" = "Οποιοσδήποτε μπορεί να συμμετάσχει σε αυτό το δωμάτιο"; "screen_create_room_access_section_anyone_option_title" = "Οποιοσδήποτε"; "screen_create_room_access_section_header" = "Πρόσβαση Δωματίου"; "screen_create_room_access_section_knocking_option_description" = "Οποιοσδήποτε μπορεί να ζητήσει να συμμετάσχει στο δωμάτιο, αλλά ένας διαχειριστής ή συντονιστής θα πρέπει να αποδεχθεί το αίτημα"; "screen_create_room_access_section_knocking_option_title" = "Αίτημα συμμετοχής"; -"screen_join_room_cancel_knock_action" = "Cancel request"; +"screen_join_room_cancel_knock_action" = "Ακύρωση αιτήματος"; "screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; "screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; "screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; "screen_join_room_knock_message_description" = "Μήνυμα (προαιρετικό)"; -"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; -"screen_join_room_knock_sent_title" = "Request to join sent"; +"screen_join_room_knock_sent_description" = "Θα λάβεις πρόσκληση για συμμετοχή στο δωμάτιο εάν το αίτημά σου γίνει αποδεκτό."; +"screen_join_room_knock_sent_title" = "Το αίτημα συμμετοχής στάλθηκε"; "screen_pinned_timeline_empty_state_description" = "Πάτα σε ένα μήνυμα και επέλεξε «%1$@» για να συμπεριληφθεί εδώ."; "screen_pinned_timeline_empty_state_headline" = "Καρφίτσωσε σημαντικά μηνύματα, ώστε να μπορούν να εντοπιστούν εύκολα"; "screen_reset_encryption_password_error" = "Συνέβη ένα άγνωστο σφάλμα. Έλεγξε ότι ο κωδικός πρόσβασης του λογαριασμού σου είναι σωστός και δοκίμασε ξανά."; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Φόρτωση μηνύματος..."; "screen_room_pinned_banner_view_all_button_title" = "Προβολή Όλων"; "screen_room_details_pinned_events_row_title" = "Καρφιτσωμένα μηνύματα"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Το μήνυμα δεν στάλθηκε επειδή η επαληθευμένη ταυτότητα του χρήστη %1$@ έχει αλλάξει."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Το μήνυμα δεν στάλθηκε επειδή ο χρήστης %1$@ δεν έχει επαληθεύσει όλες τις συσκευές."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Το μήνυμα δεν στάλθηκε επειδή δεν έχεις επαληθεύσει τουλάχιστον μία από τις συσκευές σου."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Πρόκειται να δημιουργήσεις έναν λογαριασμό στο %@"; "screen_advanced_settings_developer_mode" = "Λειτουργία προγραμματιστή"; "screen_advanced_settings_developer_mode_description" = "Ενεργοποίησε την πρόσβαση σε δυνατότητες και λειτουργικότητα για προγραμματιστές."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Απενεργοποίησε τον επεξεργαστή εμπλουτισμένου κειμένου για να πληκτρολογήσεις Markdown χειροκίνητα."; "screen_advanced_settings_send_read_receipts" = "Αποδεικτικά ανάγνωσης"; "screen_advanced_settings_send_read_receipts_description" = "Εάν απενεργοποιηθεί, τα αποδεικτικά ανάγνωσης δεν θα στέλνονται σε κανέναν. Θα εξακολουθείς να λαμβάνεις αποδεικτικά ανάγνωσης από άλλους χρήστες."; @@ -446,9 +456,12 @@ "screen_change_server_title" = "Επέλεξε το διακομιστή σου"; "screen_chat_backup_key_backup_action_disable" = "Απενεργοποίηση αντιγράφων ασφαλείας"; "screen_chat_backup_key_backup_action_enable" = "Ενεργοποίηση αντιγράφων ασφαλείας"; -"screen_chat_backup_key_backup_description" = "Η δημιουργία αντιγράφων ασφαλείας διασφαλίζει ότι δεν θα χάσεις το ιστορικό μηνυμάτων σου. %1$@."; -"screen_chat_backup_key_backup_title" = "Αντίγραφο ασφαλείας"; +"screen_chat_backup_key_backup_description" = "Αποθήκευσε την κρυπτογραφική σου ταυτότητα και τα κλειδιά μηνυμάτων με ασφάλεια στον διακομιστή. Αυτό θα σου επιτρέψει να δεις το ιστορικό μηνυμάτων σου σε οποιεσδήποτε νέες συσκευές. %1$@."; +"screen_chat_backup_key_backup_title" = "Χώρος αποθήκευσης κλειδιού"; +"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" = "Αλλαγή κλειδιού ανάκτησης"; +"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" = "Το αντίγραφο ασφαλείας της συνομιλίας σου δεν είναι συγχρονισμένο αυτήν τη στιγμή."; "screen_chat_backup_recovery_action_setup" = "Ρύθμιση ανάκτησης"; "screen_chat_backup_recovery_action_setup_description" = "Απόκτησε πρόσβαση στα κρυπτογραφημένα σου μηνύματα εάν χάσεις όλες τις συσκευές σου ή έχεις αποσυνδεθεί από το %1$@ παντού."; @@ -470,10 +483,10 @@ "screen_create_poll_title" = "Δημιουργία Δημοσκόπησης"; "screen_create_room_action_create_room" = "Νέο δωμάτιο"; "screen_create_room_error_creating_room" = "Παρουσιάστηκε σφάλμα κατά τη δημιουργία του δωματίου"; -"screen_create_room_private_option_description" = "Τα μηνύματα σε αυτό το δωμάτιο είναι κρυπτογραφημένα. Η κρυπτογράφηση δεν μπορεί να απενεργοποιηθεί αργότερα."; -"screen_create_room_private_option_title" = "Ιδιωτικό δωμάτιο (μόνο με πρόσκληση)"; -"screen_create_room_public_option_description" = "Τα μηνύματα δεν είναι κρυπτογραφημένα και ο καθένας μπορεί να τα διαβάσει. Μπορείς να ενεργοποιήσεις την κρυπτογράφηση αργότερα."; -"screen_create_room_public_option_title" = "Δημόσιο δωμάτιο (οποιοσδήποτε)"; +"screen_create_room_private_option_description" = "Μόνο άτομα που έχουν προσκληθεί μπορούν να έχουν πρόσβαση σε αυτό το δωμάτιο. Όλα τα μηνύματα είναι κρυπτογραφημένα από άκρο σε άκρο."; +"screen_create_room_private_option_title" = "Ιδιωτικό δωμάτιο"; +"screen_create_room_public_option_description" = "Ο καθένας μπορεί να βρει αυτό το δωμάτιο.\nΜπορείς να το αλλάξεις ανά πάσα στιγμή στις ρυθμίσεις δωματίου."; +"screen_create_room_public_option_title" = "Δημόσιο δωμάτιο"; "screen_create_room_topic_label" = "Θέμα (προαιρετικό)"; "screen_deactivate_account_confirmation_dialog_content" = "Παρακαλώ επιβεβαίωσε ότι θες να απενεργοποιήσεις τον λογαριασμό σου. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί."; "screen_deactivate_account_delete_all_messages" = "Διαγραφή όλων των μηνυμάτων μου"; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Ο κωδικός επαλήθευσής σου"; "screen_recovery_key_change_description" = "Απόκτησε ένα νέο κλειδί ανάκτησης εάν έχεις χάσει το υπάρχον. Αφού αλλάξεις το κλειδί ανάκτησης, το παλιό δεν θα λειτουργεί πλέον."; "screen_recovery_key_change_generate_key" = "Δημιουργία νέου κλειδιού ανάκτησης"; -"screen_recovery_key_change_generate_key_description" = "Βεβαιώσου ότι μπορείς να αποθηκεύσεις το κλειδί ανάκτησης σε ασφαλές μέρος"; "screen_recovery_key_change_success" = "Το κλειδί ανάκτησης άλλαξε"; "screen_recovery_key_change_title" = "Αλλαγή κλειδιού ανάκτησης;"; "screen_recovery_key_confirm_create_new_recovery_key" = "Δημιουργία νέου κλειδιού ανάκτησης"; @@ -635,14 +647,14 @@ "screen_recovery_key_copied_to_clipboard" = "Αντιγράφηκε το κλειδί ανάκτησης"; "screen_recovery_key_generating_key" = "Δημιουργία..."; "screen_recovery_key_save_action" = "Αποθήκευση κλειδιού ανάκτησης"; -"screen_recovery_key_save_description" = "Γράψε το κλειδί ανάκτησης κάπου ασφαλές ή αποθήκευσέ το σε έναν διαχειριστή κωδικών πρόσβασης."; +"screen_recovery_key_save_description" = "Γράψε αυτό το κλειδί ανάκτησης κάπου ασφαλές, όπως έναν διαχειριστή κωδικών πρόσβασης, μια κρυπτογραφημένη σημείωση ή ένα φυσικό χρηματοκιβώτιο."; "screen_recovery_key_save_key_description" = "Πάτα για να αντιγράψεις το κλειδί ανάκτησης"; "screen_recovery_key_save_title" = "Αποθήκευσε το κλειδί ανάκτησης"; "screen_recovery_key_setup_confirmation_description" = "Δεν θα μπορείς να αποκτήσεις πρόσβαση στο νέο κλειδί ανάκτησης μετά από αυτό το βήμα."; "screen_recovery_key_setup_confirmation_title" = "Έχεις αποθηκεύσει το κλειδί ανάκτησης;"; "screen_recovery_key_setup_description" = "Το αντίγραφο ασφαλείας της συνομιλίας σου προστατεύεται από ένα κλειδί ανάκτησης. Εάν χρειαστείς ένα νέο κλειδί ανάκτησης μετά την εγκατάσταση, μπορείς να δημιουργήσεις ξανά επιλέγοντας «Αλλαγή κλειδιού ανάκτησης»."; "screen_recovery_key_setup_generate_key" = "Δημιουργία κλειδιού ανάκτησης"; -"screen_recovery_key_setup_generate_key_description" = "Βεβαιώσου ότι μπορείς να αποθηκεύσεις το κλειδί ανάκτησης κάπου ασφαλές"; +"screen_recovery_key_setup_generate_key_description" = "Μην το μοιραστείς με κανέναν!"; "screen_recovery_key_setup_success" = "Επιτυχής ρύθμιση ανάκτησης"; "screen_recovery_key_setup_title" = "Ρύθμιση ανάκτησης"; "screen_report_content_block_user_hint" = "Επέλεξε εάν θες να αποκρύψεις όλα τα τρέχοντα και μελλοντικά μηνύματα από αυτόν τον χρήστη"; @@ -824,9 +836,13 @@ "screen_session_verification_ready_subtitle" = "Σύγκρινε ένα μοναδικό σύνολο emojis."; "screen_session_verification_request_accepted_subtitle" = "Σύγκρινε τα μοναδικά emoji και σιγουρέψου ότι εμφανίζονται με την ίδια σειρά."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; -"screen_session_verification_request_title" = "Verification requested"; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; +"screen_session_verification_request_title" = "Ζητήθηκε επαλήθευση"; "screen_session_verification_they_dont_match" = "Δεν ταιριάζουν"; "screen_session_verification_they_match" = "Ταιριάζουν"; "screen_session_verification_waiting_to_accept_subtitle" = "Αποδέξου το αίτημα για να ξεκινήσεις τη διαδικασία επαλήθευσης στην άλλη συνεδρία σου για να συνεχίσεις."; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Το Matrix είναι ένα ανοιχτό δίκτυο για ασφαλή, αποκεντρωμένη επικοινωνία."; "screen_notification_settings_mentions_section_title" = "Αναφορές"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Προσπάθησε ξανά"; +"screen_recovery_key_change_generate_key_description" = "Μην το μοιραστείς με κανέναν!"; "screen_recovery_key_confirm_title" = "Εισήγαγε το κλειδί ανάκτησης"; "screen_report_content_block_user" = "Αποκλεισμός χρήστη"; "screen_reset_encryption_password_placeholder" = "Εισαγωγή..."; diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 9a1b11ba00..b16f7076ba 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -388,6 +388,8 @@ "screen_account_provider_signup_title" = "You’re about to create an account on %@"; "screen_advanced_settings_developer_mode" = "Developer mode"; "screen_advanced_settings_developer_mode_description" = "Enable to have access to features and functionality for developers."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Disable the rich text editor to type Markdown manually."; "screen_advanced_settings_send_read_receipts" = "Read receipts"; "screen_advanced_settings_send_read_receipts_description" = "If turned off, your read receipts won't be sent to anyone. You will still receive read receipts from other users."; diff --git a/ElementX/Resources/Localizations/es.lproj/Localizable.strings b/ElementX/Resources/Localizations/es.lproj/Localizable.strings index 0fd74cb2f6..1c3482b773 100644 --- a/ElementX/Resources/Localizations/es.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/es.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(editado)"; "common_editing" = "Edición"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "Cifrado activado"; "common_enter_your_pin" = "Introduce tu PIN"; "common_error" = "Error"; @@ -149,6 +150,7 @@ "common_favourited" = "Marcado como favorito"; "common_file" = "Archivo"; "common_forward_message" = "Reenviar mensaje"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Imagen"; "common_in_reply_to" = "En respuesta a %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Pinned"; "common.send_to" = "Send to"; "common.you" = "You"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "La copia de seguridad del chat no está sincronizada en este momento. Debes confirmar tu clave de recuperación para mantener el acceso a la copia de seguridad del chat."; "confirm_recovery_key_banner_title" = "Confirma tu clave de recuperación"; "crash_detection_dialog_content" = "%1$@ se cerró inesperadamente la última vez que se lo usaste. ¿Quieres compartir un informe de error con nosotros?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "URL base personalizada de Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Define una URL base personalizada para Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL no válida, asegúrate de incluir el protocolo (http/https) y la dirección correcta."; +"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"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Loading message…"; "screen_room_pinned_banner_view_all_button_title" = "View All"; "screen_room_details_pinned_events_row_title" = "Pinned messages"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Estás a punto de crear una cuenta en %@"; "screen_advanced_settings_developer_mode" = "Modo desarrollador"; "screen_advanced_settings_developer_mode_description" = "Habilita para tener acceso a características y funcionalidades para desarrolladores."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Desactiva el editor de texto enriquecido para escribir Markdown manualmente."; "screen_advanced_settings_send_read_receipts" = "Confirmaciones de lectura"; "screen_advanced_settings_send_read_receipts_description" = "Si se desactiva, las confirmaciones de lectura no se enviarán a nadie. Seguirás recibiendo confirmaciones de lectura de otros usuarios."; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "Activar copia de seguridad"; "screen_chat_backup_key_backup_description" = "La copia de seguridad garantiza que no pierdas tu historial de mensajes. %1$@."; "screen_chat_backup_key_backup_title" = "Copia de seguridad"; +"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" = "Cambiar la clave de recuperación"; +"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" = "La copia de seguridad de tus chats no está sincronizada ahora mismo."; "screen_chat_backup_recovery_action_setup" = "Configurar la clave de recuperación"; "screen_chat_backup_recovery_action_setup_description" = "Accede a tus mensajes cifrados si pierdes todos tus dispositivos o cierras sesión de %1$@ en cualquier lugar."; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Your verification code"; "screen_recovery_key_change_description" = "Obtén una nueva clave de recuperación si has perdido la que tenías. Después de cambiar la clave de recuperación, la anterior dejará de funcionar."; "screen_recovery_key_change_generate_key" = "Generar una nueva clave de recuperación"; -"screen_recovery_key_change_generate_key_description" = "Asegúrate de poder guardar su clave de recuperación en un lugar seguro"; "screen_recovery_key_change_success" = "Clave de recuperación cambiada"; "screen_recovery_key_change_title" = "¿Cambiar la clave de recuperación?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Create new recovery key"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Compara un conjunto único de emojis."; "screen_session_verification_request_accepted_subtitle" = "Compara los emoji, asegurándote de que aparecen en el mismo orden."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "No coinciden"; "screen_session_verification_they_match" = "Coinciden"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix es una red abierta para una comunicación segura y descentralizada."; "screen_notification_settings_mentions_section_title" = "Menciones"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Inténtalo de nuevo"; +"screen_recovery_key_change_generate_key_description" = "Asegúrate de que puedes guardar tu clave de recuperación en algún lugar seguro"; "screen_recovery_key_confirm_title" = "Confirma tu clave de recuperación"; "screen_report_content_block_user" = "Bloquear usuario"; "screen_reset_encryption_password_placeholder" = "Ingresar..."; diff --git a/ElementX/Resources/Localizations/et.lproj/Localizable.strings b/ElementX/Resources/Localizations/et.lproj/Localizable.strings index 49c57f1249..43cb396385 100644 --- a/ElementX/Resources/Localizations/et.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/et.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(muudetud)"; "common_editing" = "Muutmine"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Krüptimine"; "common_encryption_enabled" = "Krüptimine on kasutusel"; "common_enter_your_pin" = "Sisesta oma PIN-kood"; "common_error" = "Viga"; @@ -149,6 +150,7 @@ "common_favourited" = "Lemmikuks määratud"; "common_file" = "Fail"; "common_forward_message" = "Edasta sõnum"; +"common_frequently_used" = "Sagedasti kasutatud"; "common_gif" = "GIF"; "common_image" = "Pilt"; "common_in_reply_to" = "Vastuseks kasutajale %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Esiletõstetud"; "common.send_to" = "Saada kasutajale"; "common.you" = "Sina"; +"common_unable_to_decrypt_insecure_device" = "Saadetud ebaturvalisest seadmest"; +"common_unable_to_decrypt_verification_violation" = "Saatja verifitseeritud identiteet on muutunud"; "confirm_recovery_key_banner_message" = "Sinu vestluste varukoopia pole hetkel sünkroonis. Säilitamaks ligipääsu vestluse varukoopiale palun sisesta oma taastevõti."; "confirm_recovery_key_banner_title" = "Sisesta oma taastevõti"; "crash_detection_dialog_content" = "%1$@ jooksis kokku viimati, kui seda kasutasid. Kas tahaksid selle kohta meile veateate saata?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "Element Calli kohandatud teenuseaadress"; "screen_advanced_settings_element_call_base_url_description" = "Seadista kohandatud teenuseaadress Element Calli jaoks."; "screen_advanced_settings_element_call_base_url_validation_error" = "Vigane url. Palun vaata, et url algaks protokolliga (http/https) ning aadress ise oleks ka õige."; +"screen_create_room_room_address_section_footer" = "Selleks, et see jututuba oleks nähtav jututubade avalikus kataloogis, sa vajad jututoa aadressi."; +"screen_create_room_room_address_section_title" = "Jututoa aadress"; +"screen_create_room_room_visibility_section_title" = "Jututoa nähtavus"; "screen_create_room_access_section_anyone_option_description" = "Kõik võivad selle jututoaga liituda"; "screen_create_room_access_section_anyone_option_title" = "Kõik"; "screen_create_room_access_section_header" = "Ligipääs jututoale"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Laadime sõnumit…"; "screen_room_pinned_banner_view_all_button_title" = "Näita kõiki"; "screen_room_details_pinned_events_row_title" = "Esiletõstetud sõnumid"; +"screen_roomlist_knock_event_sent_description" = "Liitumispäring on saadetud"; "screen_timeline_item_menu_send_failure_changed_identity" = "Sõnum on saatmata, kuna kasutaja %1$@ verifitseeritud identiteet on muutunud."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Sõnum on saatmata, kuna %1$@ pole verifitseerinud kõiki oma seadmeid."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Kuna sa pole üks või enamgi oma seadet verifitseerinud, siis sinu sõnum on saatmata."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Sa oled loomas kasutajakontot %@ teenuses"; "screen_advanced_settings_developer_mode" = "Arendaja valikud"; "screen_advanced_settings_developer_mode_description" = "Selle eelistuse sisselülitamisel lisanduvad rakendusse arendaja tööks vajalikud valikud."; +"screen_advanced_settings_media_compression_description" = "Optimeeri üleslaadimiseks"; +"screen_advanced_settings_media_compression_title" = "Meedia"; "screen_advanced_settings_rich_text_editor_description" = "Kui soovid Markdown-vormingut käsitsi lisada, siis lülita vormindatud teksti toimeti välja."; "screen_advanced_settings_send_read_receipts" = "Lugemisteatised"; "screen_advanced_settings_send_read_receipts_description" = "Kui lülitad selle valiku välja, siis mitte keegi enam ei saa sinult lugemisteatisi. Küll aga saad sina teiste kasutajate lugemisteatisi."; @@ -446,9 +456,12 @@ "screen_change_server_title" = "Vali oma server"; "screen_chat_backup_key_backup_action_disable" = "Lülita võtmete varundamine välja"; "screen_chat_backup_key_backup_action_enable" = "Lülita võtmete varundamine sisse"; -"screen_chat_backup_key_backup_description" = "Varundamine tagab, et sinu sõnumite ajalugu on alati loetav. %1$@."; -"screen_chat_backup_key_backup_title" = "Varundus"; +"screen_chat_backup_key_backup_description" = "Salvesta oma krüptoidentiteet ja sõnumite krüptovõtmed turvaliselt serveris. See tagab, et sinu sõnumite ajalugu on alati loetav, ka kõikides uutes seadmetes. %1$@."; +"screen_chat_backup_key_backup_title" = "Krüptovõtmete varundus"; +"screen_chat_backup_key_storage_toggle_description" = "Laadi siin seadmes leiduvad võtmed üles"; +"screen_chat_backup_key_storage_toggle_title" = "Luba krüptovõtmete salvestamine"; "screen_chat_backup_recovery_action_change" = "Muuda taastevõtit"; +"screen_chat_backup_recovery_action_change_description" = "Kui sa oled kaotanud ligipääsu kõikidele oma olemasolevatele seadmetele, siis sa saad taastevõtme abil taastada ligipääsu oma krüptoidentiteedile ja sõnumite ajaloole."; "screen_chat_backup_recovery_action_confirm_description" = "Sinu vestluste krüptograafia varukoopia pole hetkel enam sünkroonis."; "screen_chat_backup_recovery_action_setup" = "Seadista krüptovõtmete varundus"; "screen_chat_backup_recovery_action_setup_description" = "Säilita ligipääs oma krüptitud sõnumitele ka siis, kui sa kaotad kõik oma seadmed ja/või logid kõikjal välja rakendusest %1$@."; @@ -470,10 +483,10 @@ "screen_create_poll_title" = "Loo küsitlus"; "screen_create_room_action_create_room" = "Uus jututuba"; "screen_create_room_error_creating_room" = "Jututoa loomisel tekkis viga"; -"screen_create_room_private_option_description" = "Sõnumid siin jututoas on krüptitud ja seda ei saa hiljem välja lülitada."; -"screen_create_room_private_option_title" = "Privaatne jututuba (liitumine vaid kutsega)"; -"screen_create_room_public_option_description" = "Sõnumid pole krüptitud ja neid saavad kõik lugeda. Soovi korral saad hiljem krüptimise sisse lülitada."; -"screen_create_room_public_option_title" = "Avalik jututuba (avatud kõigile)"; +"screen_create_room_private_option_description" = "Ligipääs siia jututuppa on vaid kutse alusel. Kõik sõnumid siin jututoas on läbivalt krüptitud."; +"screen_create_room_private_option_title" = "Privaatne jututuba"; +"screen_create_room_public_option_description" = "Kõik saavad seda jututuba leida.\nSa võid seda jututoa seadistustest alati muuta."; +"screen_create_room_public_option_title" = "Avalik jututuba"; "screen_create_room_topic_label" = "Teema (kui soovid lisada)"; "screen_deactivate_account_confirmation_dialog_content" = "Palun kinnita uuesti, et soovid eemaldada oma konto kasutusest"; "screen_deactivate_account_delete_all_messages" = "Kustuta kõik minu sõnumid"; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Sinu verifitseerimiskood"; "screen_recovery_key_change_description" = "Kui oled vana taastevõtme kaotanud, siis loo uus. Peale seda muudatust vana taastevõti enam ei tööta."; "screen_recovery_key_change_generate_key" = "Loo uus taastevõti"; -"screen_recovery_key_change_generate_key_description" = "Palun hoia taastevõtit turvaliselt, näiteks vana kooli seifis või digitaalses salasõnalaekas"; "screen_recovery_key_change_success" = "Taastevõti on muudetud"; "screen_recovery_key_change_title" = "Kas muudame taastevõtme?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Loo uus taastevõti"; @@ -635,14 +647,14 @@ "screen_recovery_key_copied_to_clipboard" = "Taastevõti on kopeeritud lõikelauale"; "screen_recovery_key_generating_key" = "Loome..."; "screen_recovery_key_save_action" = "Salvesta taastevõti"; -"screen_recovery_key_save_description" = "Palun märgi taastevõti üles ja hoia seda turvaliselt, näiteks vana kooli seifis või digitaalses salasõnalaekas"; +"screen_recovery_key_save_description" = "Palun märgi taastevõti üles ja hoia seda turvaliselt, näiteks digitaalses salasõnalaekas, krüptitud märkmetes või vana kooli seifis."; "screen_recovery_key_save_key_description" = "Taastevõtme kopeerimiseks puuduta"; "screen_recovery_key_save_title" = "Salvesta oma taastevõti"; "screen_recovery_key_setup_confirmation_description" = "Peale seda sammu sul pole enam ligipääsu oma taastevõtmele."; "screen_recovery_key_setup_confirmation_title" = "Kas sa oled oma taastevõtme talletanud?"; "screen_recovery_key_setup_description" = "Sinu vestluste varundus on krüptitud taastevõtmega. Kui peale muutusi peaks vaja olema uut taastevõtit, siis palun kasuta valikut „Loo taastevõti“"; "screen_recovery_key_setup_generate_key" = "Loo oma taastevõti"; -"screen_recovery_key_setup_generate_key_description" = "Palun hoia taastevõtit turvaliselt, näiteks vana kooli seifis või digitaalses salasõnalaekas"; +"screen_recovery_key_setup_generate_key_description" = "Ära jaga seda kellegagi"; "screen_recovery_key_setup_success" = "Andmete taastamise seadistamine õnnestus"; "screen_recovery_key_setup_title" = "Seadista andmete taastamine"; "screen_report_content_block_user_hint" = "Vali see eelistus, kui sa soovid peita selle kasutaja kõik senised ja tulevased sõnumid"; @@ -815,7 +827,7 @@ "screen_session_verification_compare_numbers_title" = "Võrdle numbreid"; "screen_session_verification_complete_subtitle" = "Sinu uus sessioon on nüüd verifitseeritud. Sellel sessioonil on nüüd ligipääs sinu krüptitud sõnumitele ja teised osapooled näevad teda usaldusväärsena."; "screen_session_verification_enter_recovery_key" = "Sisesta taastevõti"; -"screen_session_verification_failed_subtitle" = "Kas verifitseerimine aegus, teine osapool keeldus vastanast või tekkis vastuste mittevastavus."; +"screen_session_verification_failed_subtitle" = "Kas verifitseerimine aegus, teine osapool keeldus vastamast või tekkis vastuste mittevastavus."; "screen_session_verification_open_existing_session_subtitle" = "Saamaks ligipääsu krüptitud sõnumite ajaloole tõesta et tegemist on sinuga."; "screen_session_verification_open_existing_session_title" = "Ava olemasolev sessioon"; "screen_session_verification_positive_button_canceled" = "Proovi verifitseerimist uuesti"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Võrdle unikaalset emojide kombinatsiooni"; "screen_session_verification_request_accepted_subtitle" = "Võrdle unikaalset emojide kombinatsiooni ning kontrolli, et nad on täpselt samas järjekorras."; "screen_session_verification_request_details_timestamp" = "Sisselogitud"; +"screen_session_verification_request_failure_subtitle" = "Kas verifitseerimine aegus, teine osapool keeldus vastamast või tekkis vastuste mittevastavus."; +"screen_session_verification_request_failure_title" = "Verifitseerimine ei õnnestunud"; "screen_session_verification_request_footer" = "Jätka vaid siis, kui sina algatasid verifitseerimise."; "screen_session_verification_request_subtitle" = "Hoidmaks oma sõnumiajalugu turvatuna verifitseeri teine seade."; +"screen_session_verification_request_success_subtitle" = "Võid nüüd sõnumeid oma teises seadmes turvaliselt saata ja vastu võtta."; +"screen_session_verification_request_success_title" = "Seade on verifitseeritud"; "screen_session_verification_request_title" = "Verifitseerimispäring"; "screen_session_verification_they_dont_match" = "Nad ei klapi omavahel"; "screen_session_verification_they_match" = "Nad klapivad omavahel"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix on avatud võrk turvalise ja hajutatud suhtluse jaoks."; "screen_notification_settings_mentions_section_title" = "Mainimiste alusel"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Proovi uuesti"; +"screen_recovery_key_change_generate_key_description" = "Ära jaga seda kellegagi"; "screen_recovery_key_confirm_title" = "Sisesta oma taastevõti"; "screen_report_content_block_user" = "Blokeeri kasutaja"; "screen_reset_encryption_password_placeholder" = "Sisesta..."; diff --git a/ElementX/Resources/Localizations/fa.lproj/Localizable.strings b/ElementX/Resources/Localizations/fa.lproj/Localizable.strings index b995c0ae8c..fbff44abcb 100644 --- a/ElementX/Resources/Localizations/fa.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/fa.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(ویراسته)"; "common_editing" = "ویرایش"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "رمزنگاری به کار افتاده"; "common_enter_your_pin" = "پینتان را وارد کنید"; "common_error" = "خطا"; @@ -149,6 +150,7 @@ "common_favourited" = "برگزیده"; "common_file" = "پرونده"; "common_forward_message" = "هدایت پیام"; +"common_frequently_used" = "Frequently used"; "common_gif" = "جیف"; "common_image" = "تصویر"; "common_in_reply_to" = "در پاسخ به %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "سنجاق شده"; "common.send_to" = "فرستادن به"; "common.you" = "شما"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup."; "confirm_recovery_key_banner_title" = "ورود کلید بازیابیتان"; "crash_detection_dialog_content" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "نشانی پایهٔ تماس المنتی سفارشی"; "screen_advanced_settings_element_call_base_url_description" = "تنظمی نشانی پایه‌‌ای سفارشی برای تماس المنتی."; "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"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "بار کردن پشام‌ها…"; "screen_room_pinned_banner_view_all_button_title" = "نمایش همه"; "screen_room_details_pinned_events_row_title" = "پیام‌های سنجاق شده"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "دارید حسابی روی %@ می‌سازید"; "screen_advanced_settings_developer_mode" = "حالت توسعه‌دهنده"; "screen_advanced_settings_developer_mode_description" = "Enable to have access to features and functionality for developers."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "از کار انداختن ویرایشگر متن غنی یا نوشتن دستی مارک‌دون."; "screen_advanced_settings_send_read_receipts" = "رسید‌های خواندن"; "screen_advanced_settings_send_read_receipts_description" = "If turned off, your read receipts won't be sent to anyone. You will still receive read receipts from other users."; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "روشن کردن پشتیبان"; "screen_chat_backup_key_backup_description" = "پشتیبان‌ها اطمینان می‌دهند که تاریخچهٔ پیام‌هایتان را از دست نمی‌دهید. %1$@."; "screen_chat_backup_key_backup_title" = "پشتیبان گیری"; +"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" = "تغییر کلید بازیابی"; +"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" = "پشتیبان گپتان از هم‌گام بودن در آمده."; "screen_chat_backup_recovery_action_setup" = "برپایی بازیابی"; "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."; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "کد تأییدتان"; "screen_recovery_key_change_description" = "گرفتن کلید بازیابی جدید در صورت فراموشی کلید کنونی. پس از تغییر دادن کلید بازیابیتان، کلید پیشین دیگر کار نخواهد کرد."; "screen_recovery_key_change_generate_key" = "تولید کلید بازیابی جدید"; -"screen_recovery_key_change_generate_key_description" = "اطمینان از امکان نگه داری کلید بازیابیتان در جایی امن"; "screen_recovery_key_change_success" = "کلید بازیابی تغییر کرد"; "screen_recovery_key_change_title" = "تغییر کلید بازیابی؟"; "screen_recovery_key_confirm_create_new_recovery_key" = "ایجاد کلید بازیابی جدید"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "مقایسهٔ مجموعه‌ای یکتا از شکلک‌ها."; "screen_session_verification_request_accepted_subtitle" = "شکلک‌ها را مقایسه کنید، از ترتیب نمایش آنان نیز مطمئن شوید."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "مطابق نیستند"; "screen_session_verification_they_match" = "مطابقند"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "ماتریکس شبکه‌ای بار برای ارتباطات نامتمرکز و امن است."; "screen_notification_settings_mentions_section_title" = "اشاره‌ها"; "screen_qr_code_login_invalid_scan_state_retry_button" = "تلاش دوباره"; +"screen_recovery_key_change_generate_key_description" = "اطمینان از امکان نگه داری کلید بازیابیتان در جایی امن"; "screen_recovery_key_confirm_title" = "ورود کلید بازیابیتان"; "screen_report_content_block_user" = "انسداد کاربر"; "screen_reset_encryption_password_placeholder" = "ورود…"; diff --git a/ElementX/Resources/Localizations/fr.lproj/Localizable.strings b/ElementX/Resources/Localizations/fr.lproj/Localizable.strings index f2b1734bc5..58ced059ca 100644 --- a/ElementX/Resources/Localizations/fr.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/fr.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(modifié)"; "common_editing" = "Édition"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "Chiffrement activé"; "common_enter_your_pin" = "Saisissez votre code PIN"; "common_error" = "Erreur"; @@ -149,6 +150,7 @@ "common_favourited" = "Favorisé"; "common_file" = "Fichier"; "common_forward_message" = "Transférer le message"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Image"; "common_in_reply_to" = "En réponse à %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Épinglé"; "common.send_to" = "Envoyer vers"; "common.you" = "Vous"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "La sauvegarde des conversations est désynchronisée. Vous devez confirmer la clé de récupération pour accéder à votre historique."; "confirm_recovery_key_banner_title" = "Confirmer votre clé de récupération"; "crash_detection_dialog_content" = "%1$@ s’est arrêté la dernière fois qu’il a été utilisé. Souhaitez-vous partager un rapport d’incident avec nous ?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "URL de base pour Element Call personnalisée"; "screen_advanced_settings_element_call_base_url_description" = "Configurer une URL de base pour Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL invalide, assurez-vous d’inclure le protocol (http/https) et l’adresse correcte."; +"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" = "Tout le monde peut rejoindre ce salon"; "screen_create_room_access_section_anyone_option_title" = "Tout le monde"; "screen_create_room_access_section_header" = "Accès au salon"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Chargement du message..."; "screen_room_pinned_banner_view_all_button_title" = "Voir tout"; "screen_room_details_pinned_events_row_title" = "Messages épinglés"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Le message n'a pas été envoyé car l'identité vérifiée de %1$@ a changé."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Le message n'a pas été envoyé car %1$@ n'a pas vérifié tous ses appareils."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message non envoyé car vous n'avez pas vérifié tous vos appareils."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Vous êtes sur le point de créer un compte sur %@"; "screen_advanced_settings_developer_mode" = "Mode développeur"; "screen_advanced_settings_developer_mode_description" = "Activer pour pouvoir accéder aux fonctionnalités destinées aux développeurs."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Désactivez l’éditeur de texte enrichi pour saisir manuellement du Markdown."; "screen_advanced_settings_send_read_receipts" = "Accusés de lecture"; "screen_advanced_settings_send_read_receipts_description" = "En cas de désactivation, vos accusés de lecture ne seront pas envoyés aux autres membres. Vous verrez toujours les accusés des autres membres."; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "Activer la sauvegarde"; "screen_chat_backup_key_backup_description" = "La sauvegarde assure que vous ne perdiez pas l’historique des discussions. %1$@."; "screen_chat_backup_key_backup_title" = "Sauvegarde"; +"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" = "Changer la clé de récupération"; +"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" = "La sauvegarde des discussions est désynchronisée."; "screen_chat_backup_recovery_action_setup" = "Configurer la récupération"; "screen_chat_backup_recovery_action_setup_description" = "Accédez à vos messages chiffrés si vous perdez tous vos appareils ou que vous êtes déconnectés de %1$@ partout."; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Votre code de vérification"; "screen_recovery_key_change_description" = "Obtenez une nouvelle clé de récupération dans le cas où vous avez oublié l’ancienne. Après le changement, l’ancienne clé ne sera plus utilisable."; "screen_recovery_key_change_generate_key" = "Générer une nouvelle clé"; -"screen_recovery_key_change_generate_key_description" = "Assurez-vous de conserver la clé dans un endroit sûr"; "screen_recovery_key_change_success" = "Clé de récupération modifée"; "screen_recovery_key_change_title" = "Changer la clé de récupération?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Créer une nouvelle clé de récupération"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Comparer un groupe unique d’Emojis."; "screen_session_verification_request_accepted_subtitle" = "Comparez les emoji uniques en veillant à ce qu’ils apparaissent dans le même ordre."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Continuez uniquement si c'est vous qui avez commencé cette vérification."; "screen_session_verification_request_subtitle" = "Vérifiez l'autre appareil pour sécuriser l'historique de vos messages."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Vérification demandée"; "screen_session_verification_they_dont_match" = "Ils ne correspondent pas"; "screen_session_verification_they_match" = "Ils correspondent"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix est un réseau ouvert pour une communication sécurisée et décentralisée."; "screen_notification_settings_mentions_section_title" = "Mentions"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Essayer à nouveau"; +"screen_recovery_key_change_generate_key_description" = "Assurez-vous de conserver la clé dans un endroit sûr"; "screen_recovery_key_confirm_title" = "Confirmer votre clé de récupération"; "screen_report_content_block_user" = "Bloquer l’utilisateur"; "screen_reset_encryption_password_placeholder" = "Saisissez la clé ici…"; diff --git a/ElementX/Resources/Localizations/hu.lproj/Localizable.strings b/ElementX/Resources/Localizations/hu.lproj/Localizable.strings index cf0a7dd602..a25c0b972f 100644 --- a/ElementX/Resources/Localizations/hu.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/hu.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(szerkesztve)"; "common_editing" = "Szerkesztés"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "Titkosítás engedélyezve"; "common_enter_your_pin" = "Adja meg a PIN-kódját"; "common_error" = "Hiba"; @@ -149,6 +150,7 @@ "common_favourited" = "Kedvencnek jelölve"; "common_file" = "Fájl"; "common_forward_message" = "Üzenet továbbítása"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Kép"; "common_in_reply_to" = "Válasz erre: %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Kitűzve"; "common.send_to" = "Címzett"; "common.you" = "Ön"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "A csevegés biztonsági mentése nincs szinkronban. Meg kell erősítenie a helyreállítási kulcsát, hogy továbbra is hozzáférjen a csevegés biztonsági mentéséhez."; "confirm_recovery_key_banner_title" = "Helyreállítási kulcs megerősítése"; "crash_detection_dialog_content" = "Az %1$@ összeomlott a legutóbbi használata óta. Megosztja velünk az összeomlás-jelentést?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "Egyéni Element Call alapwebcím"; "screen_advanced_settings_element_call_base_url_description" = "Egyéni alapwebcím beállítása az Element Callhoz."; "screen_advanced_settings_element_call_base_url_validation_error" = "Érvénytelen webcím, győződjön meg arról, hogy szerepel-e benne a protokoll (http/https), és hogy helyes-e a cím."; +"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" = "Bárki csatlakozhat ehhez a szobához"; "screen_create_room_access_section_anyone_option_title" = "Bárki"; "screen_create_room_access_section_header" = "Szobahozzáférés"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Üzenet betöltése…"; "screen_room_pinned_banner_view_all_button_title" = "Összes megtekintése"; "screen_room_details_pinned_events_row_title" = "Kitűzött üzenetek"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Az üzenet nem lett elküldve, mert %1$@ ellenőrzött személyazonossága megváltozott."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Az üzenet nem lett elküldve, mert %1$@ nem ellenőrizte az összes eszközét."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Az üzenet nem lett elküldve, mert egy vagy több eszközét nem ellenőrizte."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Hamarosan létrehoz egy fiókot itt: %@"; "screen_advanced_settings_developer_mode" = "Fejlesztői mód"; "screen_advanced_settings_developer_mode_description" = "Engedélyezze, hogy elérje a fejlesztőknek szánt funkciókat."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "A formázott szöveges szerkesztő letiltása, hogy kézzel írhasson Markdownt."; "screen_advanced_settings_send_read_receipts" = "Olvasási visszaigazolások"; "screen_advanced_settings_send_read_receipts_description" = "Ha ki van kapcsolva, az olvasási visszaigazolások nem lesznek elküldve senkinek. A többi felhasználó olvasási visszaigazolását továbbra is meg fogja kapni."; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "Biztonsági mentés bekapcsolása"; "screen_chat_backup_key_backup_description" = "A biztonsági mentés biztosítja, hogy ne veszítse el az üzenetelőzményeit. %1$@."; "screen_chat_backup_key_backup_title" = "Biztonsági mentés"; +"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" = "Helyreállítási kulcs módosítása"; +"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" = "A csevegéselőzményei nincsenek szinkronban."; "screen_chat_backup_recovery_action_setup" = "Helyreállítás beállítása"; "screen_chat_backup_recovery_action_setup_description" = "Szerezzen hozzáférést a titkosított üzeneteihez, ha elvesztette az összes eszközét, vagy ha mindenütt kijelentkezett az %1$@ből."; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Az Ön ellenőrzőkódja"; "screen_recovery_key_change_description" = "Szerezzen új helyreállítási kulcsot, ha elvesztette a meglévőt. A helyreállítása kulcsa módosítása után a régi már nem fog működni."; "screen_recovery_key_change_generate_key" = "Új helyreállítási kulcs előállítása"; -"screen_recovery_key_change_generate_key_description" = "Gondoskodjon arról, hogy biztonságos helyen tárolja a helyreállítási kulcsát"; "screen_recovery_key_change_success" = "Helyreállítási kulcs lecserélve"; "screen_recovery_key_change_title" = "Módosítja a helyreállítási kulcsot?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Új helyreállítási kulcs létrehozása"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Egyedi emodzsik összehasonlítása."; "screen_session_verification_request_accepted_subtitle" = "Hasonlítsa össze az egyedi emodzsikat, meggyőződve arról, hogy azonos a sorrendjük."; "screen_session_verification_request_details_timestamp" = "Bejelentkezve"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Csak akkor folytassa, ha Ön kezdeményezte ezt az ellenőrzést."; "screen_session_verification_request_subtitle" = "Az üzenetelőzmények biztonságának megőrzése érdekében ellenőrizze a másik eszközt."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Ellenőrzés kérve"; "screen_session_verification_they_dont_match" = "Nem egyeznek"; "screen_session_verification_they_match" = "Megegyeznek"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "A Matrix egy nyitott hálózat a biztonságos, decentralizált kommunikációhoz."; "screen_notification_settings_mentions_section_title" = "Említések"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Próbálja újra"; +"screen_recovery_key_change_generate_key_description" = "Gondoskodjon arról, hogy biztonságos helyen tárolja a helyreállítási kulcsát"; "screen_recovery_key_confirm_title" = "Helyreállítási kulcs megerősítése"; "screen_report_content_block_user" = "Felhasználó letiltása"; "screen_reset_encryption_password_placeholder" = "Megadás…"; diff --git a/ElementX/Resources/Localizations/id.lproj/Localizable.strings b/ElementX/Resources/Localizations/id.lproj/Localizable.strings index df2a4c7cb7..b959cf848e 100644 --- a/ElementX/Resources/Localizations/id.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/id.lproj/Localizable.strings @@ -26,7 +26,7 @@ "action_back" = "Kembali"; "action_call" = "Panggil"; "action_cancel" = "Batal"; -"action_cancel_for_now" = "Cancel for now"; +"action_cancel_for_now" = "Batalkan untuk saat ini"; "action_choose_photo" = "Pilih foto"; "action_clear" = "Hapus"; "action_close" = "Tutup"; @@ -75,7 +75,7 @@ "action_ok" = "Oke"; "action_open_settings" = "Pengaturan"; "action_open_with" = "Buka dengan"; -"action_pin" = "Pin"; +"action_pin" = "Sematkan"; "action_quick_reply" = "Balas cepat"; "action_quote" = "Kutip"; "action_react" = "Bereaksi"; @@ -86,7 +86,7 @@ "action_report_bug" = "Laporkan kutu"; "action_report_content" = "Laporkan Konten"; "action_reset" = "Atur ulang"; -"action_reset_identity" = "Reset identity"; +"action_reset_identity" = "Atur ulang identitas"; "action_retry" = "Coba lagi"; "action_retry_decryption" = "Coba dekripsi ulang"; "action_save" = "Simpan"; @@ -107,16 +107,16 @@ "action_take_photo" = "Ambil foto"; "action_tap_for_options" = "Ketuk untuk opsi"; "action_try_again" = "Coba lagi"; -"action_unpin" = "Unpin"; -"action_view_in_timeline" = "View in timeline"; +"action_unpin" = "Lepaskan sematan"; +"action_view_in_timeline" = "Lihat di lini masa"; "action_view_source" = "Tampilkan sumber"; "action_yes" = "Ya"; -"banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; -"banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; -"banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; -"banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; -"banner.set_up_recovery.title" = "Set up recovery"; +"banner_migrate_to_native_sliding_sync_action" = "Keluar & Tingkatkan"; +"banner_migrate_to_native_sliding_sync_description" = "Server Anda kini mendukung protokol baru yang lebih cepat. Keluar dan masuk lagi untuk memperbarui sekarang. Melakukan hal ini sekarang akan membantu Anda menghindari keluar paksa saat protokol lama dihapus nantinya."; +"banner_migrate_to_native_sliding_sync_force_logout_title" = "Homeserver Anda tidak lagi mendukung protokol lama. Silakan keluar dan masuk kembali untuk terus menggunakan aplikasi."; +"banner_migrate_to_native_sliding_sync_title" = "Peningkatan tersedia"; +"banner.set_up_recovery.content" = "Buat kunci pemulihan baru yang dapat digunakan untuk memulihkan riwayat pesan terenkripsi Anda jika Anda kehilangan akses ke perangkat Anda."; +"banner.set_up_recovery.title" = "Siapkan pemulihan"; "common_about" = "Tentang"; "common_acceptable_use_policy" = "Kebijakan penggunaan wajar"; "common_advanced_settings" = "Pengaturan tingkat lanjut"; @@ -139,6 +139,7 @@ "common_edited_suffix" = "(disunting)"; "common_editing" = "Penyuntingan"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "Enkripsi diaktifkan"; "common_enter_your_pin" = "Masukkan PIN Anda"; "common_error" = "Eror"; @@ -149,6 +150,7 @@ "common_favourited" = "Difavoritkan"; "common_file" = "Berkas"; "common_forward_message" = "Teruskan pesan"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Gambar"; "common_in_reply_to" = "Membalas kepada %1$@"; @@ -237,10 +239,12 @@ "common_waiting_for_decryption_key" = "Menunggu pesan ini"; "common.copied_to_clipboard" = "Copied to clipboard"; "common.do_not_show_this_again" = "Jangan tampilkan ini lagi"; -"common.open_source_licenses" = "Open source licenses"; -"common.pinned" = "Pinned"; +"common.open_source_licenses" = "Lisensi sumber terbuka"; +"common.pinned" = "Disematkan"; "common.send_to" = "Kirim ke"; "common.you" = "You"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Cadangan percakapan Anda saat ini tidak tersinkron. Anda perlu mengonfirmasi kunci pemulihan Anda untuk tetap memiliki akses ke cadangan percakapan Anda."; "confirm_recovery_key_banner_title" = "Konfirmasi kunci pemulihan Anda"; "crash_detection_dialog_content" = "%1$@ mengalami kemogokan saat terakhir kali digunakan. Apakah Anda ingin berbagi laporan kerusakan dengan kami?"; @@ -277,12 +281,12 @@ "error_no_compatible_app_found" = "Tidak ada aplikasi yang kompatibel yang ditemukan untuk menangani tindakan ini."; "error_some_messages_have_not_been_sent" = "Beberapa pesan belum terkirim"; "error_unknown" = "Maaf, terjadi kesalahan"; -"event_shield_reason_authenticity_not_guaranteed" = "The authenticity of this encrypted message can't be guaranteed on this device."; -"event_shield_reason_previously_verified" = "Encrypted by a previously-verified user."; -"event_shield_reason_sent_in_clear" = "Not encrypted."; -"event_shield_reason_unknown_device" = "Encrypted by an unknown or deleted device."; -"event_shield_reason_unsigned_device" = "Encrypted by a device not verified by its owner."; -"event_shield_reason_unverified_identity" = "Encrypted by an unverified user."; +"event_shield_reason_authenticity_not_guaranteed" = "Keaslian pesan terenkripsi ini tidak dapat dijamin pada perangkat ini."; +"event_shield_reason_previously_verified" = "Dienkripsi oleh pengguna yang telah diverifikasi sebelumnya."; +"event_shield_reason_sent_in_clear" = "Tidak dienkripsi."; +"event_shield_reason_unknown_device" = "Dienkripsi oleh perangkat yang tidak dikenal atau dihapus."; +"event_shield_reason_unsigned_device" = "Dienkripsi oleh perangkat yang tidak diverifikasi oleh pemiliknya."; +"event_shield_reason_unverified_identity" = "Dienkripsi oleh pengguna yang tidak terverifikasi."; "full_screen_intent_banner_message" = "Untuk memastikan Anda tidak melewatkan panggilan penting, silakan ubah pengaturan Anda untuk memperbolehkan notifikasi layar penuh ketika ponsel Anda terkunci."; "full_screen_intent_banner_title" = "Tingkatkan pengalaman panggilan Anda"; "invite_friends_rich_title" = "🔐️ Bergabunglah dengan saya di %1$@"; @@ -307,7 +311,7 @@ "notification_room_invite_body" = "Mengundang Anda untuk bergabung ke ruangan"; "notification_room_invite_body_with_sender" = "%1$@ invited you to join the room"; "notification_sender_me" = "Saya"; -"notification_sender_mention_reply" = "%1$@ mentioned or replied"; +"notification_sender_mention_reply" = "%1$@ disebut atau dibalas"; "notification_test_push_notification_content" = "Anda sedang melihat pemberitahuan ini! Klik saya!"; "notification_ticker_text_dm" = "%1$@: %2$@"; "notification_ticker_text_group" = "%1$@: %2$@ %3$@"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "URL dasar Element Call khusus"; "screen_advanced_settings_element_call_base_url_description" = "Tetapkan URL dasar khusus untuk Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL tidak valid, pastikan Anda menyertakan protokol (http/https) dan alamat yang benar."; +"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"; @@ -352,25 +359,26 @@ "screen_join_room_knock_message_description" = "Message (optional)"; "screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; "screen_join_room_knock_sent_title" = "Request to join sent"; -"screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; -"screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; -"screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; +"screen_pinned_timeline_empty_state_description" = "Tekan pesan dan pilih “%1$@” untuk disertakan di sini."; +"screen_pinned_timeline_empty_state_headline" = "Sematkan pesan penting agar mudah ditemukan"; +"screen_reset_encryption_password_error" = "Terjadi kesalahan yang tidak diketahui. Harap periksa apakah kata sandi akun Anda sudah benar dan coba lagi."; "screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; "screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; "screen_resolve_send_failure_changed_identity_title" = "Your message was not sent because %1$@’s verified identity has changed"; -"screen_resolve_send_failure_unsigned_device_primary_button_title" = "Send message anyway"; -"screen_resolve_send_failure_unsigned_device_subtitle" = "%1$@ is using one or more unverified devices. You can send the message anyway, or you can cancel for now and try again later after %2$@ has verified all their devices."; -"screen_resolve_send_failure_unsigned_device_title" = "Your message was not sent because %1$@ has not verified all devices"; +"screen_resolve_send_failure_unsigned_device_primary_button_title" = "Kirim pesan saja"; +"screen_resolve_send_failure_unsigned_device_subtitle" = "%1$@ menggunakan satu atau beberapa perangkat yang belum diverifikasi. Anda tetap dapat mengirim pesan, atau Anda dapat membatalkan untuk saat ini dan mencoba lagi nanti setelah %2$@ telah memverifikasi semua perangkat mereka."; +"screen_resolve_send_failure_unsigned_device_title" = "Pesan Anda tidak terkirim karena %1$@ belum memverifikasi semua perangkat"; "screen_resolve_send_failure_you_unsigned_device_subtitle" = "One or more of your devices are unverified. You can send the message anyway, or you can cancel for now and try again later after you have verified all of your devices."; "screen_resolve_send_failure_you_unsigned_device_title" = "Your message was not sent because you have not verified one or more of your devices"; "screen_room_mentions_at_room_subtitle" = "Beri tahu seluruh ruangan"; -"screen_room_pinned_banner_indicator" = "%1$@ of %2$@"; -"screen_room_pinned_banner_indicator_description" = "%1$@ Pinned messages"; -"screen_room_pinned_banner_loading_description" = "Loading message…"; -"screen_room_pinned_banner_view_all_button_title" = "View All"; -"screen_room_details_pinned_events_row_title" = "Pinned messages"; -"screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; -"screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; +"screen_room_pinned_banner_indicator" = "%1$@ dari %2$@"; +"screen_room_pinned_banner_indicator_description" = "%1$@ Pesan yang disematkan"; +"screen_room_pinned_banner_loading_description" = "Memuat pesan…"; +"screen_room_pinned_banner_view_all_button_title" = "Lihat Semua"; +"screen_room_details_pinned_events_row_title" = "Pesan yang disematkan"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; +"screen_timeline_item_menu_send_failure_changed_identity" = "Pesan tidak terkirim karena identitas terverifikasi %1$@ telah berubah."; +"screen_timeline_item_menu_send_failure_unsigned_device" = "Pesan tidak terkirim karena %1$@ belum memverifikasi semua perangkat."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; "screen_account_provider_form_hint" = "Alamat homeserver"; "screen_account_provider_form_notice" = "Masukkan istilah pencarian atau alamat domain."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Anda akan membuat akun di %@"; "screen_advanced_settings_developer_mode" = "Mode pengembang"; "screen_advanced_settings_developer_mode_description" = "Aktifkan untuk mengakses fitur dan fungsi untuk para pengembang."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Nonaktifkan penyunting teks kaya untuk mengetik Markdown secara manual."; "screen_advanced_settings_send_read_receipts" = "Laporan dibaca"; "screen_advanced_settings_send_read_receipts_description" = "Jika dimatikan, laporan dibaca Anda tidak akan dikirim kepada siapa pun. Anda masih akan menerima laporan dibaca dari pengguna lain."; @@ -446,9 +456,12 @@ "screen_change_server_title" = "Pilih server Anda"; "screen_chat_backup_key_backup_action_disable" = "Matikan pencadangan"; "screen_chat_backup_key_backup_action_enable" = "Nyalakan pencadangan"; -"screen_chat_backup_key_backup_description" = "Pencadangan memastikan bahwa Anda tidak akan kehilangan riwayat pesan Anda. %1$@."; -"screen_chat_backup_key_backup_title" = "Pencadangan"; +"screen_chat_backup_key_backup_description" = "Simpan identitas kriptografi Anda dan kunci-kunci pesan secara aman di server. Ini akan memungkinkan Anda untuk melihat riwayat pesan Anda di perangkat yang baru. %1$@."; +"screen_chat_backup_key_backup_title" = "Penyimpanan kunci"; +"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" = "Ubah kunci pemulihan"; +"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" = "Pencadangan percakapan Anda saat ini tidak tersinkron."; "screen_chat_backup_recovery_action_setup" = "Siapkan pemulihan"; "screen_chat_backup_recovery_action_setup_description" = "Dapatkan akses ke pesan terenkripsi Anda jika Anda kehilangan semua perangkat Anda atau keluar dari %1$@ di mana pun."; @@ -470,10 +483,10 @@ "screen_create_poll_title" = "Buat pemungutan suara"; "screen_create_room_action_create_room" = "Ruangan baru"; "screen_create_room_error_creating_room" = "Terjadi kesalahan saat membuat ruangan"; -"screen_create_room_private_option_description" = "Pesan di ruangan ini dienkripsi. Enkripsi tidak dapat dinonaktifkan setelahnya."; -"screen_create_room_private_option_title" = "Ruangan pribadi (hanya undangan)"; -"screen_create_room_public_option_description" = "Pesan tidak dienkripsi dan siapa pun dapat membacanya. Anda dapat mengaktifkan enkripsi di kemudian hari."; -"screen_create_room_public_option_title" = "Ruang publik (siapa saja)"; +"screen_create_room_private_option_description" = "Hanya orang-orang yang diundang dapat mengakses ruangan ini. Semua pesan terenkripsi secara ujung ke ujung."; +"screen_create_room_private_option_title" = "Ruangan pribadi"; +"screen_create_room_public_option_description" = "Siapa pun dapat mencari ruangan ini.\nAnda dapat mengubah ini kapan pun dalam pengaturan ruangan."; +"screen_create_room_public_option_title" = "Ruangan publik"; "screen_create_room_topic_label" = "Topik (opsional)"; "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"; @@ -493,18 +506,18 @@ "screen_edit_profile_error_title" = "Tidak dapat memperbarui profil"; "screen_edit_profile_title" = "Sunting profil"; "screen_edit_profile_updating_details" = "Memperbarui profil…"; -"screen_encryption_reset_action_continue_reset" = "Continue reset"; -"screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose any message history that’s stored only on the server"; -"screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; -"screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; -"screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; -"screen_identity_confirmation_cannot_confirm" = "Can't confirm?"; +"screen_encryption_reset_action_continue_reset" = "Lanjutkan pengaturan ulang"; +"screen_encryption_reset_bullet_1" = "Detail akun, kontak, preferensi, dan daftar obrolan Anda akan disimpan"; +"screen_encryption_reset_bullet_2" = "Anda akan kehilangan riwayat pesan yang hanya disimpan di server"; +"screen_encryption_reset_bullet_3" = "Anda perlu memverifikasi semua perangkat dan kontak yang ada lagi"; +"screen_encryption_reset_footer" = "Hanya atur ulang identitas Anda jika Anda tidak memiliki akses ke perangkat lain yang masuk dan Anda kehilangan kunci pemulihan."; +"screen_encryption_reset_title" = "Tidak dapat mengonfirmasi? Anda perlu mengatur ulang identitas Anda."; +"screen_identity_confirmation_cannot_confirm" = "Tidak dapat mengonfirmasi?"; "screen_identity_confirmation_create_new_recovery_key" = "Buat kunci pemulihan baru"; "screen_identity_confirmation_subtitle" = "Verifikasi perangkat ini untuk menyiapkan perpesanan aman."; "screen_identity_confirmation_title" = "Konfirmasi bahwa ini Anda"; "screen_identity_confirmation_use_another_device" = "Gunakan perangkat lain"; -"screen_identity_confirmation_use_recovery_key" = "Use recovery key"; +"screen_identity_confirmation_use_recovery_key" = "Gunakan kunci pemulihan"; "screen_identity_confirmed_subtitle" = "Sekarang Anda dapat membaca atau mengirim pesan dengan aman, dan siapa pun yang mengobrol dengan Anda juga dapat mempercayai perangkat ini."; "screen_identity_confirmed_title" = "Perangkat terverifikasi"; "screen_identity_waiting_on_other_device" = "Menunggu di perangkat lain…"; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Kode verifikasi Anda"; "screen_recovery_key_change_description" = "Dapatkan kunci pemulihan yang baru jika Anda kehilangan kunci pemulihan saat ini. Setelah mengganti kunci pemulihan Anda, yang lama tidak akan bekerja lagi."; "screen_recovery_key_change_generate_key" = "Buat kunci pemulihan baru"; -"screen_recovery_key_change_generate_key_description" = "Pastikan Anda dapat menyimpan kunci pemulihan Anda di tempat yang aman"; "screen_recovery_key_change_success" = "Kunci pemulihan diganti"; "screen_recovery_key_change_title" = "Ubah kunci pemulihan?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Buat kunci pemulihan baru"; @@ -635,26 +647,26 @@ "screen_recovery_key_copied_to_clipboard" = "Kunci pemulihan disalin"; "screen_recovery_key_generating_key" = "Membuat…"; "screen_recovery_key_save_action" = "Simpan kunci pemulihan"; -"screen_recovery_key_save_description" = "Tuliskan kunci pemulihan Anda di tempat yang aman atau simpan di pengelola kata sandi."; +"screen_recovery_key_save_description" = "Tuliskan kunci pemulihan ini di tempat yang aman, seperti pengelola kata sandi, catatan terenkripsi, atau brankas fisik."; "screen_recovery_key_save_key_description" = "Ketuk untuk menyalin kunci pemulihan"; "screen_recovery_key_save_title" = "Simpan kunci pemulihan Anda"; "screen_recovery_key_setup_confirmation_description" = "Anda tidak akan dapat mengakses kunci pemulihan Anda setelah langkah ini."; "screen_recovery_key_setup_confirmation_title" = "Apakah Anda sudah menyimpan kunci pemulihan Anda?"; "screen_recovery_key_setup_description" = "Pencadangan percakapan Anda sedang dilindungi oleh sebuah kunci pemulihan. Jika Anda perlu kunci pemulihan yang baru setelah penyiapan, Anda dapat membuat ulang dengan memilih 'Ubah kunci pemulihan'."; "screen_recovery_key_setup_generate_key" = "Buat kunci pemulihan Anda"; -"screen_recovery_key_setup_generate_key_description" = "Pastikan Anda dapat menyimpan kunci pemulihan Anda di tempat yang aman"; +"screen_recovery_key_setup_generate_key_description" = "Jangan bagikan ini kepada siapa pun!"; "screen_recovery_key_setup_success" = "Penyiapan pemulihan berhasil"; "screen_recovery_key_setup_title" = "Siapkan pemulihan"; "screen_report_content_block_user_hint" = "Centang jika Anda ingin menyembunyikan semua pesan saat ini dan yang akan datang dari pengguna ini"; "screen_report_content_explanation" = "Pesan ini akan dilaporkan ke administrator homeserver Anda. Mereka tidak akan dapat membaca pesan terenkripsi apa pun."; "screen_report_content_hint" = "Alasan melaporkan konten ini"; -"screen_reset_encryption_confirmation_alert_action" = "Yes, reset now"; -"screen_reset_encryption_confirmation_alert_subtitle" = "This process is irreversible."; -"screen_reset_encryption_confirmation_alert_title" = "Are you sure you want to reset your identity?"; -"screen_reset_encryption_password_subtitle" = "Confirm that you want to reset your identity."; -"screen_reset_encryption_password_title" = "Enter your account password to continue"; -"screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; -"screen_reset_identity_confirmation_title" = "Can't confirm? Go to your account to reset your identity."; +"screen_reset_encryption_confirmation_alert_action" = "Ya, atur ulang sekarang"; +"screen_reset_encryption_confirmation_alert_subtitle" = "Proses ini tidak dapat diurungkan."; +"screen_reset_encryption_confirmation_alert_title" = "Apakah Anda yakin ingin mengatur ulang identitas Anda?"; +"screen_reset_encryption_password_subtitle" = "Konfirmasikan bahwa Anda ingin mengatur ulang identitas Anda."; +"screen_reset_encryption_password_title" = "Masukkan kata sandi akun Anda untuk melanjutkan"; +"screen_reset_identity_confirmation_subtitle" = "Anda akan pergi ke akun %1$@ Anda untuk mengatur ulang identitas Anda. Setelah itu Anda akan dibawa kembali ke aplikasi."; +"screen_reset_identity_confirmation_title" = "Tidak dapat mengonfirmasi? Buka akun Anda untuk mengatur ulang identitas Anda."; "screen_room_alias_resolver_resolve_alias_failure" = "Gagal menyelesaikan alias ruangan."; "screen_room_attachment_source_camera" = "Kamera"; "screen_room_attachment_source_camera_video" = "Rekam video"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Bandingkan satu set emoji yang unik."; "screen_session_verification_request_accepted_subtitle" = "Bandingkan emoji unik, dan pastikan emoji tersebut muncul dalam urutan yang sama."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Mereka tidak cocok"; "screen_session_verification_they_match" = "Mereka cocok"; @@ -910,12 +926,12 @@ "state_event_room_name_removed_by_you" = "Anda menghapus nama ruangan"; "state_event_room_none" = "%1$@ tidak membuat perubahan"; "state_event_room_none_by_you" = "Anda tidak membuat perubahan"; -"state_event_room_pinned_events_changed" = "%1$@ changed the pinned messages"; -"state_event_room_pinned_events_changed_by_you" = "You changed the pinned messages"; -"state_event_room_pinned_events_pinned" = "%1$@ pinned a message"; -"state_event_room_pinned_events_pinned_by_you" = "You pinned a message"; -"state_event_room_pinned_events_unpinned" = "%1$@ unpinned a message"; -"state_event_room_pinned_events_unpinned_by_you" = "You unpinned a message"; +"state_event_room_pinned_events_changed" = "%1$@ mengubah pesan yang disematkan"; +"state_event_room_pinned_events_changed_by_you" = "Anda mengubah pesan yang disematkan"; +"state_event_room_pinned_events_pinned" = "%1$@ menyematkan pesan"; +"state_event_room_pinned_events_pinned_by_you" = "Anda menyematkan pesan"; +"state_event_room_pinned_events_unpinned" = "%1$@ melepas sematan pesan"; +"state_event_room_pinned_events_unpinned_by_you" = "Anda melepas sematan pesan"; "state_event_room_reject" = "%1$@ menolak undangan"; "state_event_room_reject_by_you" = "Anda menolak undangan"; "state_event_room_remove" = "%1$@ mengeluarkan %2$@"; @@ -983,7 +999,7 @@ "notification_invitation_action_reject" = "Tolak"; "notification_room_action_mark_as_read" = "Tandai sebagai dibaca"; "notification_room_action_quick_reply" = "Balas cepat"; -"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; +"screen_pinned_timeline_screen_title_empty" = "Pesan yang disematkan"; "screen_room_mentions_at_room_title" = "Semua orang"; "screen_account_provider_change" = "Ubah penyedia akun"; "screen_account_provider_signin_subtitle" = "Di sinilah percakapan Anda akan berlangsung — sama seperti Anda menggunakan penyedia surel untuk menyimpan surel Anda."; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix adalah jaringan terbuka untuk komunikasi yang aman dan terdesentralisasi."; "screen_notification_settings_mentions_section_title" = "Sebutan"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Coba lagi"; +"screen_recovery_key_change_generate_key_description" = "Jangan bagikan ini kepada siapa pun!"; "screen_recovery_key_confirm_title" = "Konfirmasi kunci pemulihan Anda"; "screen_report_content_block_user" = "Blokir pengguna"; "screen_reset_encryption_password_placeholder" = "Masukkan..."; diff --git a/ElementX/Resources/Localizations/id.lproj/Localizable.stringsdict b/ElementX/Resources/Localizations/id.lproj/Localizable.stringsdict index b4e966a3b9..2787aa3503 100644 --- a/ElementX/Resources/Localizations/id.lproj/Localizable.stringsdict +++ b/ElementX/Resources/Localizations/id.lproj/Localizable.stringsdict @@ -180,10 +180,8 @@ NSStringPluralRuleType NSStringFormatValueTypeKey d - one - %1$d Pinned message other - %1$d Pinned messages + %1$d Pesan yang disematkan screen_room_member_list_header_title diff --git a/ElementX/Resources/Localizations/it.lproj/Localizable.strings b/ElementX/Resources/Localizations/it.lproj/Localizable.strings index d9a92ad9c6..69b8ea3d0f 100644 --- a/ElementX/Resources/Localizations/it.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/it.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(modificato)"; "common_editing" = "Modifica in corso"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "Crittografia abilitata"; "common_enter_your_pin" = "Inserisci il PIN"; "common_error" = "Errore"; @@ -149,6 +150,7 @@ "common_favourited" = "Preferita"; "common_file" = "File"; "common_forward_message" = "Inoltra messaggio"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Immagine"; "common_in_reply_to" = "In risposta a %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Fissato"; "common.send_to" = "Invia a"; "common.you" = "Tu"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Il backup della chat non è attualmente sincronizzato. Devi confermare la chiave di recupero per mantenere l'accesso al backup della chat."; "confirm_recovery_key_banner_title" = "Inserisci la chiave di recupero"; "crash_detection_dialog_content" = "%1$@ si è chiuso inaspettatamente l'ultima volta che è stato usato. Vuoi condividere con noi un rapporto sull'arresto anomalo?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "URL base di Element Call personalizzato"; "screen_advanced_settings_element_call_base_url_description" = "Imposta un URL di base personalizzato per Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL non valido, assicurati di includere il protocollo (http/https) e l'indirizzo corretto."; +"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"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Caricamento messaggio…"; "screen_room_pinned_banner_view_all_button_title" = "Mostra tutti"; "screen_room_details_pinned_events_row_title" = "Messaggi fissati"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Messaggio non inviato perché l'identità verificata di %1$@ è cambiata."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Messaggio non inviato perché %1$@ non ha verificato tutti i dispositivi."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Messaggio non inviato perché non hai verificato uno o più dispositivi."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Stai per creare un account su %@"; "screen_advanced_settings_developer_mode" = "Modalità sviluppatore"; "screen_advanced_settings_developer_mode_description" = "Attiva per avere accesso alle funzionalità per sviluppatori."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Disattiva l'editor di testo avanzato per scrivere manualmente in Markdown"; "screen_advanced_settings_send_read_receipts" = "Ricevute di visualizzazione"; "screen_advanced_settings_send_read_receipts_description" = "Se disattivato, le tue ricevute di visualizzazione non verranno inviate a nessuno. Riceverai comunque ricevute di visualizzazione da altri utenti."; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "Attiva il backup"; "screen_chat_backup_key_backup_description" = "Il backup ti garantisce di non perdere la cronologia dei messaggi. %1$@."; "screen_chat_backup_key_backup_title" = "Backup"; +"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" = "Cambia la chiave di recupero"; +"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" = "Il backup delle conversazioni non è attualmente sincronizzato."; "screen_chat_backup_recovery_action_setup" = "Configura il recupero"; "screen_chat_backup_recovery_action_setup_description" = "Ottieni l'accesso ai tuoi messaggi cifrati se perdi tutti i tuoi dispositivi o se sei disconnesso da %1$@ ovunque."; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Il tuo codice di verifica"; "screen_recovery_key_change_description" = "Ottieni una nuova chiave di recupero se hai perso quella esistente. Dopo averla cambiata, quella vecchia non funzionerà più."; "screen_recovery_key_change_generate_key" = "Genera una nuova chiave di recupero"; -"screen_recovery_key_change_generate_key_description" = "Assicurati di conservare la chiave di recupero in un posto sicuro"; "screen_recovery_key_change_success" = "Chiave di recupero cambiata"; "screen_recovery_key_change_title" = "Cambiare la chiave di recupero?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Crea una nuova chiave di recupero"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Confronta un set unico di emoji."; "screen_session_verification_request_accepted_subtitle" = "Confronta le emoji uniche, assicurandoti che appaiano nello stesso ordine."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Non corrispondono"; "screen_session_verification_they_match" = "Corrispondono"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix è una rete aperta per comunicazioni sicure e decentralizzate."; "screen_notification_settings_mentions_section_title" = "Menzioni"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Riprova"; +"screen_recovery_key_change_generate_key_description" = "Assicurati di conservare la chiave di recupero in un posto sicuro"; "screen_recovery_key_confirm_title" = "Inserisci la chiave di recupero"; "screen_report_content_block_user" = "Blocca utente"; "screen_reset_encryption_password_placeholder" = "Inserisci..."; diff --git a/ElementX/Resources/Localizations/ka.lproj/Localizable.strings b/ElementX/Resources/Localizations/ka.lproj/Localizable.strings index ad341b3866..5ff6c8560a 100644 --- a/ElementX/Resources/Localizations/ka.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ka.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(რედაქტირებულია)"; "common_editing" = "რედაქტირება"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "დაშიფვრა ჩართულია"; "common_enter_your_pin" = "შეიყვანეთ თქვენი PIN"; "common_error" = "შეცდომა"; @@ -149,6 +150,7 @@ "common_favourited" = "Favourited"; "common_file" = "ფაილი"; "common_forward_message" = "შეტყობინების გადაგზავნა"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "სურათი"; "common_in_reply_to" = "%1$@-ს პასუხად"; @@ -241,6 +243,8 @@ "common.pinned" = "Pinned"; "common.send_to" = "Send to"; "common.you" = "You"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "თქვენი ჩეთების სარეზერვო ასლი ამჟამად არ არის სინქრონიზებული. თქვენ უნდა შეიყვანოთ თქვენი აღდგენის გასაღები, რათა შეინარჩუნოთ წვდომა ჩეთების სარეზერვო ასლზე."; "confirm_recovery_key_banner_title" = "შეიყვანეთ აღდგენის გასაღები"; "crash_detection_dialog_content" = "%1$@ ავარიულად გაითიშა ბოლოს გამოიყენებისას. გსურთ, გამოგვიგზავნოთ ავარიული გათიშვის ჟურნალი?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "მორგებული Element-ის ზარის საბაზისო URL"; "screen_advanced_settings_element_call_base_url_description" = "დააყენეთ საბაზისო URL Element-ის ზარებისათვის."; "screen_advanced_settings_element_call_base_url_validation_error" = "არასწორი URL, გთხოვთ, დარწმუნდეთ, რომ შეიტანეთ პროტოკოლი (http/https) და სწორი მისამართი."; +"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"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Loading message…"; "screen_room_pinned_banner_view_all_button_title" = "View All"; "screen_room_details_pinned_events_row_title" = "Pinned messages"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "თქვენ აპირებთ ანგარიშის შექმნას %@-ში"; "screen_advanced_settings_developer_mode" = "დეველოპერის რეჟიმი"; "screen_advanced_settings_developer_mode_description" = "ჩართეთ დეველოპერების ფუნქციებზე წვდომა."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "გამორთეთ მდიდარი ტექსტის რედაქტორი, რათა ხელით აკრიფოთ Markdown."; "screen_advanced_settings_send_read_receipts" = "Read receipts"; "screen_advanced_settings_send_read_receipts_description" = "If turned off, your read receipts won't be sent to anyone. You will still receive read receipts from other users."; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "სარეზერვო ასლის ჩართვა"; "screen_chat_backup_key_backup_description" = "სარეზერვო ასლი უზრუნველყოფს იმას, რომ თქვენ შეტყობინებების ისტორიას არ დაკარგავთ. %1$@"; "screen_chat_backup_key_backup_title" = "სარეზერვო ასლი"; +"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" = "აღდგენის გასაღების შეცვლა"; +"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" = "თქვენი ჩატის სარეზერვო ასლი ამჟამად არ არის სინქრონიზებული."; "screen_chat_backup_recovery_action_setup" = "აღდგენის დაყენება"; "screen_chat_backup_recovery_action_setup_description" = "მიიღეთ წვდომა თქვენს დაშიფრულ შეტყობინებებზე, თუ დაკარგავთ თქვენს ყველა მოწყობილობას ან გამოხვალთ სისტემიდან %1$@-დან ყველგან."; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Your verification code"; "screen_recovery_key_change_description" = "მიიღეთ ახალი აღდგენის გასაღები, თუ დაკარგეთ არსებული. აღდგენის გასაღების შეცვლის შემდეგ, ძველი აღარ იმუშავებს."; "screen_recovery_key_change_generate_key" = "ახალი აღდგენის გასაღების შექმნა"; -"screen_recovery_key_change_generate_key_description" = "დარწმუნდით, რომ შეგიძლიათ შეინახოთ თქვენი აღდგენის გასაღები სადმე უსაფრთხო ადგილას"; "screen_recovery_key_change_success" = "აღდგენის გასაღები შეიცვალა"; "screen_recovery_key_change_title" = "გსურთ აღდგენის გასაღების შეცვლა?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Create new recovery key"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "შეადარეთ ემოციების უნიკალური ნაკრები."; "screen_session_verification_request_accepted_subtitle" = "შეადარეთ უნიკალური ემოჯი, დარწმუნდით, რომ ისინი ერთი დ იმავე თანმიმდევრობით გამოჩნდნენ."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "ისინი არ ემთხვევიან ერთმანეთს"; "screen_session_verification_they_match" = "ისინი ემთხვევიან ერთმანეთს"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix არის ღია ქსელი უსაფრთხო, დეცენტრალიზებული კომუნიკაციისთვის."; "screen_notification_settings_mentions_section_title" = "ხსენებები"; "screen_qr_code_login_invalid_scan_state_retry_button" = "ხელახლა ცდა"; +"screen_recovery_key_change_generate_key_description" = "დარწმუნდით, რომ შეგიძლიათ შეინახოთ თქვენი აღდგენის გასაღები სადმე უსაფრთხო ადგილას"; "screen_recovery_key_confirm_title" = "შეიყვანეთ აღდგენის გასაღები"; "screen_report_content_block_user" = "მომხმარებლის დაბლოკვა"; "screen_reset_encryption_password_placeholder" = "შეყვანა"; diff --git a/ElementX/Resources/Localizations/nl.lproj/Localizable.strings b/ElementX/Resources/Localizations/nl.lproj/Localizable.strings index a791abb7c3..32363dc571 100644 --- a/ElementX/Resources/Localizations/nl.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/nl.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(bewerkt)"; "common_editing" = "Bewerken"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "Encryptie ingeschakeld"; "common_enter_your_pin" = "Voer je pincode in"; "common_error" = "Fout"; @@ -149,6 +150,7 @@ "common_favourited" = "Favoriet gemarkeerd"; "common_file" = "Bestand"; "common_forward_message" = "Bericht doorsturen"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Afbeelding"; "common_in_reply_to" = "Als antwoord op %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Pinned"; "common.send_to" = "Sturen naar"; "common.you" = "You"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Je chatback-up is momenteel niet gesynchroniseerd. Je moet je herstelsleutel invoeren om toegang te behouden tot je chatback-up."; "confirm_recovery_key_banner_title" = "Voer je herstelsleutel in"; "crash_detection_dialog_content" = "%1$@ crashte de laatste keer dat het werd gebruikt. Wil je een crashrapport met ons delen?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "Aangepaste basis-URL voor Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Stel een aangepaste basis-URL in voor Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Ongeldige URL, zorg ervoor dat je het protocol (http/https) en het juiste adres invult."; +"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"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Loading message…"; "screen_room_pinned_banner_view_all_button_title" = "View All"; "screen_room_details_pinned_events_row_title" = "Pinned messages"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Je staat op het punt een account aan te maken op %@"; "screen_advanced_settings_developer_mode" = "Ontwikkelaarsmodus"; "screen_advanced_settings_developer_mode_description" = "Schakel in om toegang te krijgen tot tools en functies voor ontwikkelaars."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Schakel de uitgebreide tekstverwerker uit om Markdown handmatig te typen."; "screen_advanced_settings_send_read_receipts" = "Leesbevestigingen"; "screen_advanced_settings_send_read_receipts_description" = "Indien uitgeschakeld worden er geen leesbevestigingen verstuurd. Je ontvangt nog steeds leesbevestigingen van andere gebruikers."; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "Back-up inschakelen"; "screen_chat_backup_key_backup_description" = "Een back-up maken zorgt ervoor dat je je berichtgeschiedenis niet verliest. %1$@."; "screen_chat_backup_key_backup_title" = "Back-up"; +"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" = "Herstelsleutel wijzigen"; +"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" = "Je chatback-up is momenteel niet gesynchroniseerd."; "screen_chat_backup_recovery_action_setup" = "Herstelmogelijkheid instellen"; "screen_chat_backup_recovery_action_setup_description" = "Krijg toegang tot je versleutelde berichten als je al je apparaten kwijtraakt of overal uit %1$@ bent uitgelogd."; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Je verificatiecode"; "screen_recovery_key_change_description" = "Maak een nieuwe herstelsleutel aan als je je bestaande kwijt bent. Nadat je je herstelsleutel hebt gewijzigd, werkt je oude herstelsleutel niet meer."; "screen_recovery_key_change_generate_key" = "Genereer een nieuwe herstelsleutel"; -"screen_recovery_key_change_generate_key_description" = "Zorg ervoor dat je je herstelsleutel op een veilige plek kunt bewaren"; "screen_recovery_key_change_success" = "Herstelsleutel gewijzigd"; "screen_recovery_key_change_title" = "Herstelsleutel wijzigen?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Maak een nieuwe herstelsleutel"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Vergelijk een unieke combinatie van emoji's."; "screen_session_verification_request_accepted_subtitle" = "Vergelijk de unieke emoji's, ze dienen in dezelfde volgorde te worden weergegeven."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Ze komen niet overeen"; "screen_session_verification_they_match" = "Ze komen overeen"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix is een open netwerk voor veilige, gedecentraliseerde communicatie."; "screen_notification_settings_mentions_section_title" = "Vermeldingen"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Probeer het opnieuw"; +"screen_recovery_key_change_generate_key_description" = "Zorg ervoor dat je je herstelsleutel op een veilige plek kunt bewaren"; "screen_recovery_key_confirm_title" = "Voer je herstelsleutel in"; "screen_report_content_block_user" = "Gebruiker blokkeren"; "screen_reset_encryption_password_placeholder" = "Voer in..."; diff --git a/ElementX/Resources/Localizations/pl.lproj/Localizable.strings b/ElementX/Resources/Localizations/pl.lproj/Localizable.strings index a8d5f00b19..a705326a60 100644 --- a/ElementX/Resources/Localizations/pl.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pl.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(edytowane)"; "common_editing" = "Edytowanie"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "Szyfrowanie włączone"; "common_enter_your_pin" = "Wprowadź kod PIN"; "common_error" = "Błąd"; @@ -149,6 +150,7 @@ "common_favourited" = "Ulubione"; "common_file" = "Plik"; "common_forward_message" = "Przekaż wiadomość"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Zdjęcie"; "common_in_reply_to" = "W odpowiedzi do %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Przypięte"; "common.send_to" = "Wyślij do"; "common.you" = "Ty"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Twoja kopia zapasowa czatu jest obecnie niezsynchronizowana. Aby zachować dostęp do kopii zapasowej czatu, musisz potwierdzić klucz odzyskiwania."; "confirm_recovery_key_banner_title" = "Wprowadź swój klucz przywracania"; "crash_detection_dialog_content" = "%1$@ uległ awarii podczas ostatniego użycia. Czy chcesz przesłać nam raport o awarii?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "Własny bazowy URL dla połączeń Element"; "screen_advanced_settings_element_call_base_url_description" = "Ustaw własny bazowy URL dla połączeń Element"; "screen_advanced_settings_element_call_base_url_validation_error" = "Nieprawidłowy adres URL, upewnij się, że zawiera protokół (http/https) i poprawny adres."; +"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"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Wczytywanie wiadomości..."; "screen_room_pinned_banner_view_all_button_title" = "Wyświetl wszystkie"; "screen_room_details_pinned_events_row_title" = "Przypięte wiadomości"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Wiadomość nie została wysłana, ponieważ tożsamość %1$@ uległa zmianie."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Wiadomość nie została wysłana, ponieważ %1$@ nie zweryfikował wszystkich urządzeń."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Wiadomość nie została wysłana, ponieważ nie zweryfikowałeś jednego lub więcej swoich urządzeń."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Zamierzasz założyć konto na %@"; "screen_advanced_settings_developer_mode" = "Tryb programisty"; "screen_advanced_settings_developer_mode_description" = "Włącz, aby uzyskać dostęp do funkcji dla deweloperów."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Wyłącz edytor tekstu bogatego, aby pisać tekst Markdown ręcznie."; "screen_advanced_settings_send_read_receipts" = "Potwierdzenia odczytania"; "screen_advanced_settings_send_read_receipts_description" = "Gdy wyłączona, Twoje potwierdzenia odczytania nie zostaną wysłane. Potwierdzenia od innych wciąż będą odbierane."; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "Włącz backup"; "screen_chat_backup_key_backup_description" = "Backup zapewnia, że nie stracisz swojej historii wiadomości. %1$@"; "screen_chat_backup_key_backup_title" = "Backup"; +"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" = "Zmień klucz przywracania"; +"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" = "Backup czatu jest niezsynchronizowany."; "screen_chat_backup_recovery_action_setup" = "Skonfiguruj przywracanie"; "screen_chat_backup_recovery_action_setup_description" = "Uzyskaj dostęp do swoich wiadomości szyfrowanych, jeśli utracisz wszystkie swoje urządzenia lub zostaniesz wylogowany z %1$@."; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Twój kod weryfikacyjny"; "screen_recovery_key_change_description" = "Uzyskaj nowy klucz przywracania, jeśli straciłeś dostęp do obecnego. Po zmianie klucza przywracania stary nie będzie już działał."; "screen_recovery_key_change_generate_key" = "Generuj nowy klucz przywracania"; -"screen_recovery_key_change_generate_key_description" = "Upewnij się, że klucz przywracania będzie trzymany w bezpiecznym miejscu"; "screen_recovery_key_change_success" = "Zmieniono klucz przywracania"; "screen_recovery_key_change_title" = "Zmienić klucz przywracania?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Utwórz nowy klucz przywracania"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Porównaj unikalny zestaw emoji."; "screen_session_verification_request_accepted_subtitle" = "Porównaj unikalne emoji, upewniając się, że pojawiły się w tej samej kolejności."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Nie pasują do siebie"; "screen_session_verification_they_match" = "Pasują do siebie"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix to otwarta sieć do bezpiecznej i zdecentralizowanej komunikacji."; "screen_notification_settings_mentions_section_title" = "Wzmianki"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Spróbuj ponownie"; +"screen_recovery_key_change_generate_key_description" = "Upewnij się, że klucz przywracania możesz przechowywać w bezpiecznym miejscu"; "screen_recovery_key_confirm_title" = "Wprowadź swój klucz przywracania"; "screen_report_content_block_user" = "Zablokuj użytkownika"; "screen_reset_encryption_password_placeholder" = "Wprowadź..."; diff --git a/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings b/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings index f14a3de2c2..d958aa6757 100644 --- a/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(editado)"; "common_editing" = "Editando"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "Criptografia ativada"; "common_enter_your_pin" = "Insira seu PIN"; "common_error" = "Erro"; @@ -149,6 +150,7 @@ "common_favourited" = "Favoritado"; "common_file" = "Arquivo"; "common_forward_message" = "Encaminhar mensagem"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Imagem"; "common_in_reply_to" = "Em resposta a %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Pinned"; "common.send_to" = "Enviar para"; "common.you" = "You"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup."; "confirm_recovery_key_banner_title" = "Insira sua chave de recuperação"; "crash_detection_dialog_content" = "%1$@ fechou inesperadamente na última vez que foi usado. Gostaria de compartilhar um relatório de falhas conosco?"; @@ -340,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" = "URL inválida, por favor verifique se o protocolo (http/https) e o endereço correto estão presentes."; +"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"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Loading message…"; "screen_room_pinned_banner_view_all_button_title" = "View All"; "screen_room_details_pinned_events_row_title" = "Pinned messages"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Você está prestes a criar uma conta em %@"; "screen_advanced_settings_developer_mode" = "Modo de desenvolvedor"; "screen_advanced_settings_developer_mode_description" = "Habilite para ter acesso a recursos e funcionalidades para desenvolvedores."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Desative o editor de rich text para digitar Markdown manualmente."; "screen_advanced_settings_send_read_receipts" = "Confirmações de leitura"; "screen_advanced_settings_send_read_receipts_description" = "Se desligado, suas confirmações de leitura não serão enviadas para ninguém. Você ainda receberá confirmações de leitura de outros usuários."; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "Ativar o backup"; "screen_chat_backup_key_backup_description" = "O backup garante que você não perca seu histórico de mensagens. %1$@."; "screen_chat_backup_key_backup_title" = "Backup"; +"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" = "Alterar chave de recuperação"; +"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" = "Seu backup das conversas está atualmente fora de sincronia."; "screen_chat_backup_recovery_action_setup" = "Configurar a recuperação"; "screen_chat_backup_recovery_action_setup_description" = "Tenha acesso às suas mensagens criptografadas se você perder todos os seus dispositivos ou for desconectado do %1$@ em qualquer lugar."; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Your verification code"; "screen_recovery_key_change_description" = "Obtenha uma nova chave de recuperação caso tenha perdido a existente. Depois de alterar sua chave de recuperação, a antiga não funcionará mais."; "screen_recovery_key_change_generate_key" = "Gere uma nova chave de recuperação"; -"screen_recovery_key_change_generate_key_description" = "Certifique-se de que você pode armazenar sua chave de recuperação em algum lugar seguro"; "screen_recovery_key_change_success" = "Chave de recuperação alterada"; "screen_recovery_key_change_title" = "Alterar chave de recuperação?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Create new recovery key"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Compare um conjunto único de emojis."; "screen_session_verification_request_accepted_subtitle" = "Compare os emojis únicos, garantindo que apareçam na mesma ordem."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Eles não combinam"; "screen_session_verification_they_match" = "Eles combinam"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "A Matrix é uma rede aberta para comunicação segura e descentralizada."; "screen_notification_settings_mentions_section_title" = "Menções"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Tente novamente"; +"screen_recovery_key_change_generate_key_description" = "Certifique-se de que você pode armazenar sua chave de recuperação em algum lugar seguro"; "screen_recovery_key_confirm_title" = "Insira sua chave de recuperação"; "screen_report_content_block_user" = "Bloquear usuário"; "screen_reset_encryption_password_placeholder" = "Inserir..."; diff --git a/ElementX/Resources/Localizations/pt.lproj/Localizable.strings b/ElementX/Resources/Localizations/pt.lproj/Localizable.strings index 5fd6ff0bfd..885001c5db 100644 --- a/ElementX/Resources/Localizations/pt.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pt.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(editada)"; "common_editing" = "A editar"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "Cifragem ativada"; "common_enter_your_pin" = "Introduz o teu PIN"; "common_error" = "Erro"; @@ -149,6 +150,7 @@ "common_favourited" = "Favoritas"; "common_file" = "Ficheiro"; "common_forward_message" = "Reencaminhar mensagem"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Imagem"; "common_in_reply_to" = "Em resposta a %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Afixado"; "common.send_to" = "Enviar para"; "common.you" = "Você"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "A tua cópia de segurança das conversas está atualmente dessincronizada. Tens de inserir a tua chave de recuperação para manteres o acesso à cópia."; "confirm_recovery_key_banner_title" = "Insere a tua chave de recuperação"; "crash_detection_dialog_content" = "A %1$@ teve uma falha da última vez que foi utilizada. Gostarias de partilhar um relatório de acidente connosco?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "URL base para Element Call personalizado"; "screen_advanced_settings_element_call_base_url_description" = "Define um URL base para a Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL inválido, certifica-te de que incluis o protocolo (http/https) e o endereço correto."; +"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" = "Qualquer pessoa pode entrar nesta sala"; "screen_create_room_access_section_anyone_option_title" = "Qualquer pessoa"; "screen_create_room_access_section_header" = "Acesso à sala"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "A carregar mensagem..."; "screen_room_pinned_banner_view_all_button_title" = "Ver todas"; "screen_room_details_pinned_events_row_title" = "Mensagens afixadas"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Mensagem não enviada porque a identidade verificada de %1$@ foi alterada."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Mensagem não enviada porque %1$@ não verificou todos os dispositivos."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Mensagem não enviada porque não verificou um ou mais dos seus dispositivos."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Irás criar uma conta em %@"; "screen_advanced_settings_developer_mode" = "Modo de programador"; "screen_advanced_settings_developer_mode_description" = "Permite o acesso a funcionalidades para programadores."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Desativa o editor de texto rico para poderes escrever Markdown manualmente."; "screen_advanced_settings_send_read_receipts" = "Recibos de leitura"; "screen_advanced_settings_send_read_receipts_description" = "Se desativada, os teus recibos de leitura não serão enviados a ninguém. Continuas a receber recibos de leitura de outros utilizadores."; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "Ativar a cópia de segurança"; "screen_chat_backup_key_backup_description" = "A cópia de segurança garante que não perdes o teu histórico de mensagens. %1$@."; "screen_chat_backup_key_backup_title" = "Cópia de segurança"; +"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" = "Alterar chave de recuperação"; +"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" = "A tua cópia de segurança das conversas está atualmente dessincronizada."; "screen_chat_backup_recovery_action_setup" = "Configurar recuperação"; "screen_chat_backup_recovery_action_setup_description" = "Obtém acesso às tuas mensagens cifradas mesmo se perderes todos os teus dispositivos ou se terminares todas as tuas sessões %1$@."; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "O teu código de verificação"; "screen_recovery_key_change_description" = "Obtém uma nova chave de recuperação se tiveres perdido a atual. Depois de a alterares, a antiga deixará de funcionar."; "screen_recovery_key_change_generate_key" = "Gerar uma nova chave de recuperação"; -"screen_recovery_key_change_generate_key_description" = "Certifica-te de que podes guardar a tua chave de recuperação num local seguro"; "screen_recovery_key_change_success" = "Chave de recuperação alterada"; "screen_recovery_key_change_title" = "Alterar a chave de recuperação?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Criar nova chave de recuperação"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Compara um conjunto único de emojis."; "screen_session_verification_request_accepted_subtitle" = "Compara os emojis únicos, certificando-te de que aparecem pela mesma ordem."; "screen_session_verification_request_details_timestamp" = "Sessão iniciada"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Continue apenas se tiver iniciado esta verificação."; "screen_session_verification_request_subtitle" = "Verifique o outro dispositivo para manter o histórico de mensagens seguro."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verificação solicitada"; "screen_session_verification_they_dont_match" = "Não correspondem"; "screen_session_verification_they_match" = "Correspondem"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "A Matrix é uma rede aberta de comunicação descentralizada e segura."; "screen_notification_settings_mentions_section_title" = "Menções"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Tentar novamente"; +"screen_recovery_key_change_generate_key_description" = "Certifica-te de que podes guardar a tua chave de recuperação num local seguro"; "screen_recovery_key_confirm_title" = "Insere a tua chave de recuperação"; "screen_report_content_block_user" = "Bloquear utilizador"; "screen_reset_encryption_password_placeholder" = "Inserir..."; diff --git a/ElementX/Resources/Localizations/ro.lproj/Localizable.strings b/ElementX/Resources/Localizations/ro.lproj/Localizable.strings index 633f7bf355..ee2a97a8a8 100644 --- a/ElementX/Resources/Localizations/ro.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ro.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(editat)"; "common_editing" = "Editare"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "Criptare activată"; "common_enter_your_pin" = "Introduceți codul PIN"; "common_error" = "Eroare"; @@ -149,6 +150,7 @@ "common_favourited" = "Favorită"; "common_file" = "Fişier"; "common_forward_message" = "Redirecționați mesajul"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Imagine"; "common_in_reply_to" = "Ca răspuns la %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Pinned"; "common.send_to" = "Trimiteți către"; "common.you" = "You"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Backup-ul pentru chat nu este sincronizat în prezent. Trebuie să confirmați cheia de recuperare pentru a menține accesul la backup."; "confirm_recovery_key_banner_title" = "Confirmați cheia de recuperare"; "crash_detection_dialog_content" = "%1$@ s-a blocat ultima dată când a fost folosit. Doriți să ne trimiteți un raport?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "Adresa URL de bază Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Setați o adresă URL de bază personalizată pentru Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL invalid, vă rugăm să vă asigurați că includeți protocolul (http/https) și adresa corectă."; +"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"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Loading message…"; "screen_room_pinned_banner_view_all_button_title" = "View All"; "screen_room_details_pinned_events_row_title" = "Pinned messages"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Sunteți pe cale să creați un cont pe %@"; "screen_advanced_settings_developer_mode" = "Modul dezvoltator"; "screen_advanced_settings_developer_mode_description" = "Activați pentru a avea acces la funcționalități pentru dezvoltatori."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Dezactivați editorul avansat pentru a tasta manual Markdown."; "screen_advanced_settings_send_read_receipts" = "Chitanțe de citire"; "screen_advanced_settings_send_read_receipts_description" = "Dacă dezactivată, chitanțele dumneavoastră de citire nu vor fi trimise nimănui. Veți primi în continuare chitanțe de citire de la alți utilizatori."; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "Activați backupul"; "screen_chat_backup_key_backup_description" = "Backup vă asigură că nu pierdeți istoricul mesajelor. %1$@."; "screen_chat_backup_key_backup_title" = "Backup"; +"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" = "Schimbați cheia de recuperare"; +"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" = "Backup-ul pentru chat nu este sincronizat în prezent."; "screen_chat_backup_recovery_action_setup" = "Configurați recuperarea"; "screen_chat_backup_recovery_action_setup_description" = "Obțineți acces la mesajele dumneavoastră criptate dacă vă pierdeți toate dispozitivele sau sunteți deconectat de la %1$@ peste tot."; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Codul dumneavoastră de verificare"; "screen_recovery_key_change_description" = "Obțineți o nouă cheie de recuperare dacă ați pierdut-o pe cea existentă. După schimbarea cheii de recuperare, cea veche nu va mai funcționa."; "screen_recovery_key_change_generate_key" = "Generați o nouă cheie de recuperare"; -"screen_recovery_key_change_generate_key_description" = "Asigurați-vă că puteți stoca cheia de recuperare undeva în siguranță"; "screen_recovery_key_change_success" = "Cheia de recuperare a fost schimbată"; "screen_recovery_key_change_title" = "Schimbați cheia de recuperare?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Creați o nouă cheie de recuperare"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Comparați un set unic de emoji-uri."; "screen_session_verification_request_accepted_subtitle" = "Comparăți emoticoalene asigurându-vă că apar în aceeași ordine."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Nu se potrivesc"; "screen_session_verification_they_match" = "Se potrivesc"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix este o rețea deschisă pentru o comunicare sigură și descentralizată."; "screen_notification_settings_mentions_section_title" = "Mențiuni"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Încercați din nou"; +"screen_recovery_key_change_generate_key_description" = "Asigurați-vă că puteți stoca cheia de recuperare undeva în siguranță"; "screen_recovery_key_confirm_title" = "Confirmați cheia de recuperare"; "screen_report_content_block_user" = "Blocați utilizatorul"; "screen_reset_encryption_password_placeholder" = "Introduceți..."; diff --git a/ElementX/Resources/Localizations/ru.lproj/Localizable.strings b/ElementX/Resources/Localizations/ru.lproj/Localizable.strings index 127b6df9c7..48586797e2 100644 --- a/ElementX/Resources/Localizations/ru.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ru.lproj/Localizable.strings @@ -30,7 +30,7 @@ "action_choose_photo" = "Выбрать фото"; "action_clear" = "Очистить"; "action_close" = "Закрыть"; -"action_complete_verification" = "Полная проверка"; +"action_complete_verification" = "Завершите подтверждение"; "action_confirm" = "Подтвердить"; "action_confirm_password" = "Подтвердите пароль"; "action_continue" = "Продолжить"; @@ -39,7 +39,7 @@ "action_copy_link_to_message" = "Скопировать ссылку в сообщение"; "action_create" = "Создать"; "action_create_a_room" = "Создать комнату"; -"action_deactivate" = "Деактивировать"; +"action_deactivate" = "Отключить"; "action_deactivate_account" = "Отключить учётную запись"; "action_decline" = "Отклонить"; "action_delete_poll" = "Удалить опрос"; @@ -66,7 +66,7 @@ "action_leave_conversation" = "Покинуть беседу"; "action_leave_room" = "Покинуть комнату"; "action_load_more" = "Загрузить еще"; -"action_manage_account" = "Настройки аккаунта"; +"action_manage_account" = "Настройки учетной записи"; "action_manage_devices" = "Управление устройствами"; "action_message" = "Сообщение"; "action_next" = "Далее"; @@ -139,6 +139,7 @@ "common_edited_suffix" = "(изменено)"; "common_editing" = "Редактирование"; "common_emote" = "%1$@%2$@"; +"common_encryption" = "Шифрование"; "common_encryption_enabled" = "Шифрование включено"; "common_enter_your_pin" = "Введите свой PIN-код"; "common_error" = "Ошибка"; @@ -149,6 +150,7 @@ "common_favourited" = "Избранное"; "common_file" = "Файл"; "common_forward_message" = "Переслать сообщение"; +"common_frequently_used" = "Часто используемые"; "common_gif" = "GIF"; "common_image" = "Изображения"; "common_in_reply_to" = "В ответ на %1$@"; @@ -169,7 +171,7 @@ "common_optic_id_ios" = "Оптический идентификатор"; "common_or" = "или"; "common_password" = "Пароль"; -"common_people" = "Люди"; +"common_people" = "Пользователи"; "common_permalink" = "Постоянная ссылка"; "common_permission" = "Разрешение"; "common_please_wait" = "Подождите..."; @@ -180,7 +182,7 @@ "common_privacy_policy" = "Политика конфиденциальности"; "common_reaction" = "Реакция"; "common_reactions" = "Реакции"; -"common_recovery_key" = "Ключ восстановления"; +"common_recovery_key" = "Ключ восстановления"; "common_refreshing" = "Обновление…"; "common_replying_to" = "Отвечает на %1$@"; "common_report_a_bug" = "Сообщить об ошибке"; @@ -226,8 +228,8 @@ "common_unmute" = "Вкл. звук"; "common_unsupported_event" = "Неподдерживаемое событие"; "common_username" = "Имя пользователя"; -"common_verification_cancelled" = "Проверка отменена"; -"common_verification_complete" = "Проверка завершена"; +"common_verification_cancelled" = "Подтверждение отменено"; +"common_verification_complete" = "Подтверждение завершено"; "common_verification_failed" = "Сбой проверки"; "common_verified" = "Проверено"; "common_verify_device" = "Подтверждение устройства"; @@ -241,8 +243,10 @@ "common.pinned" = "Закрепленный"; "common.send_to" = "Отправить"; "common.you" = "Вы"; +"common_unable_to_decrypt_insecure_device" = "Отправлено с незащищенного устройства"; +"common_unable_to_decrypt_verification_violation" = "Подтвержденная личность отправителя изменилась"; "confirm_recovery_key_banner_message" = "В настоящее время резервная копия ваших чатов не синхронизирована. Вам потребуется ввести свой ключ восстановления, чтобы сохранить доступ к резервной копии чатов."; -"confirm_recovery_key_banner_title" = "Введите ключ восстановления"; +"confirm_recovery_key_banner_title" = "Введите ключ восстановления"; "crash_detection_dialog_content" = "При последнем использовании %1$@ произошел сбой. Хотите поделиться отчетом о сбое?"; "crypto_identity_change_pin_violation" = "Судя по всему, идентификатор %1$@ изменился. %2$@"; "crypto_identity_change_pin_violation_new" = "Пользователь %1$@ сменил имя пользователя на %2$@. %3$@"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "Базовый URL сервера звонков Element"; "screen_advanced_settings_element_call_base_url_description" = "Задайте свой сервер Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Адрес указан неверно, удостоверьтесь, что вы указали протокол (http/https) и правильный адрес."; +"screen_create_room_room_address_section_footer" = "Чтобы эта комната была видна в каталоге общедоступных, вам необходим ее адрес"; +"screen_create_room_room_address_section_title" = "Адрес комнаты"; +"screen_create_room_room_visibility_section_title" = "Видимость комнаты"; "screen_create_room_access_section_anyone_option_description" = "Любой желающий может присоединиться к этой комнате"; "screen_create_room_access_section_anyone_option_title" = "Любой"; "screen_create_room_access_section_header" = "Доступ в комнату"; @@ -355,8 +362,8 @@ "screen_pinned_timeline_empty_state_description" = "Нажмите на сообщение и выберите “%1$@”, чтобы добавить его сюда."; "screen_pinned_timeline_empty_state_headline" = "Закрепите важные сообщения, чтобы их можно было легко найти"; "screen_reset_encryption_password_error" = "Произошла неизвестная ошибка. Проверьте правильность пароля учетной записи и повторите попытку."; -"screen_resolve_send_failure_changed_identity_primary_button_title" = "Отозвать верификацию и отправить"; -"screen_resolve_send_failure_changed_identity_subtitle" = "Вы можете отозвать свою верификацию и отправить это сообщение в любом случае или вы можете отменить ее сейчас и повторить попытку позже после повторной верификации %1$@."; +"screen_resolve_send_failure_changed_identity_primary_button_title" = "Отозвать статус и отправить"; +"screen_resolve_send_failure_changed_identity_subtitle" = "Вы можете либо отозвать свой статус подтверждения и всё равно отправить это сообщение, либо отменить его сейчас и повторить попытку после повторного подтверждения %1$@."; "screen_resolve_send_failure_changed_identity_title" = "Ваше сообщение не было отправлено, потому что изменилась подтвержденная личность %1$@"; "screen_resolve_send_failure_unsigned_device_primary_button_title" = "Отправь сообщение в любом случае"; "screen_resolve_send_failure_unsigned_device_subtitle" = "%1$@ использует одно или несколько непроверенных устройств. Вы все равно можете отправить сообщение или отменить его пока и повторить попытку позже %2$@, проверив все устройства пользователя."; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Загрузка сообщения..."; "screen_room_pinned_banner_view_all_button_title" = "Посмотреть все"; "screen_room_details_pinned_events_row_title" = "Закрепленные сообщения"; +"screen_roomlist_knock_event_sent_description" = "Запрос на присоединение отправлен"; "screen_timeline_item_menu_send_failure_changed_identity" = "Сообщение не отправлено, потому что верифицированная личность %1$@ изменилась."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Сообщение не отправлено, потому что %1$@ не проверил одно или несколько устройств."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Сообщение не отправлено, поскольку вы не подтвердили одно или несколько своих устройств."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Вы собираетесь создать учетную запись на %@"; "screen_advanced_settings_developer_mode" = "Режим разработчика"; "screen_advanced_settings_developer_mode_description" = "Предоставьте разработчикам доступ к функциям и функциональным возможностям."; +"screen_advanced_settings_media_compression_description" = "Оптимизировать для загрузки"; +"screen_advanced_settings_media_compression_title" = "Медиа"; "screen_advanced_settings_rich_text_editor_description" = "Отключить редактор форматированного текста и включить Markdown."; "screen_advanced_settings_send_read_receipts" = "Уведомления о прочтении"; "screen_advanced_settings_send_read_receipts_description" = "Если этот параметр выключен, ваш статус о прочтении не будет отображаться. Вы по-прежнему будете видеть статус о прочтении от других пользователей."; @@ -446,9 +456,12 @@ "screen_change_server_title" = "Выберите свой сервер"; "screen_chat_backup_key_backup_action_disable" = "Отключить резервное копирование"; "screen_chat_backup_key_backup_action_enable" = "Включить резервное копирование"; -"screen_chat_backup_key_backup_description" = "Резервное копирование гарантирует, что вы не потеряете историю сообщений. %1$@."; -"screen_chat_backup_key_backup_title" = "Резервное копирование"; -"screen_chat_backup_recovery_action_change" = "Изменить ключ восстановления"; +"screen_chat_backup_key_backup_description" = "Сохраните вашу криптографическую идентификацию и ключи сообщений в безопасности на сервере. Это позволит вам просматривать историю сообщений на любых новых устройствах.%1$@ ."; +"screen_chat_backup_key_backup_title" = "Хранилище ключей"; +"screen_chat_backup_key_storage_toggle_description" = "Загрузить ключи с этого устройства"; +"screen_chat_backup_key_storage_toggle_title" = "Разрешить хранение ключей"; +"screen_chat_backup_recovery_action_change" = "Изменить ключ восстановления"; +"screen_chat_backup_recovery_action_change_description" = "Если вы потеряли все существующие устройства, то сможете восстановить свою криптографическую идентификацию и историю сообщений с помощью ключа восстановления"; "screen_chat_backup_recovery_action_confirm_description" = "В настоящее время резервная копия ваших чатов не синхронизирована."; "screen_chat_backup_recovery_action_setup" = "Настроить восстановление"; "screen_chat_backup_recovery_action_setup_description" = "Получите доступ к зашифрованным сообщениям, если вы потеряете все свои устройства или выйдете из системы %1$@ отовсюду."; @@ -458,7 +471,7 @@ "screen_create_new_recovery_key_list_item_3" = "Когда вас попросят подтвердить устройство, выберите %1$@"; "screen_create_new_recovery_key_list_item_3_reset_all" = "“Сбросить все”"; "screen_create_new_recovery_key_list_item_4" = "Следуйте инструкциям, чтобы создать новый ключ восстановления"; -"screen_create_new_recovery_key_list_item_5" = "Сохраните новый ключ восстановления в менеджере паролей или зашифрованной заметке"; +"screen_create_new_recovery_key_list_item_5" = "Сохраните новый ключ восстановления в менеджере паролей или зашифрованной заметке"; "screen_create_new_recovery_key_title" = "Сбросьте шифрование вашей учетной записи с помощью другого устройства."; "screen_create_poll_add_option_btn" = "Добавить вариант"; "screen_create_poll_anonymous_desc" = "Показывать результаты только после окончания опроса"; @@ -470,20 +483,20 @@ "screen_create_poll_title" = "Создать опрос"; "screen_create_room_action_create_room" = "Создать новую комнату"; "screen_create_room_error_creating_room" = "Произошла ошибка при создании комнаты"; -"screen_create_room_private_option_description" = "Сообщения в этой комнате будут зашифрованы. Отключить шифрование позже будет невозможно."; -"screen_create_room_private_option_title" = "Частная комната (только по приглашениям)"; -"screen_create_room_public_option_description" = "Сообщения не будут зашифрованы и каждый сможет их прочитать. Шифрование можно будет включить позже."; -"screen_create_room_public_option_title" = "Общедоступная комната (для всех)"; +"screen_create_room_private_option_description" = "Доступ в эту комнату имеют только приглашенные пользователи. Все сообщения защищены сквозным шифрованием."; +"screen_create_room_private_option_title" = "Частная комната"; +"screen_create_room_public_option_description" = "Любой желающий может найти эту комнату.\nВы можете изменить это в любое время в настройках комнаты."; +"screen_create_room_public_option_title" = "Общедоступная комната"; "screen_create_room_topic_label" = "Тема (необязательно)"; -"screen_deactivate_account_confirmation_dialog_content" = "Подтвердите, что вы хотите деактивировать свою учетную запись. Это действие не может быть отменено."; +"screen_deactivate_account_confirmation_dialog_content" = "Вы уверены, что хотите отключить свою учётную запись? Данное действие не может быть отменено."; "screen_deactivate_account_delete_all_messages" = "Удалить все мои сообщения"; "screen_deactivate_account_delete_all_messages_notice" = "Предупреждение: будущие пользователи могут увидеть незавершенные разговоры."; -"screen_deactivate_account_description" = "Деактивация вашей учетной записи %1$@ означает следующее:"; -"screen_deactivate_account_description_bold_part" = "необратимый"; -"screen_deactivate_account_list_item_1" = "%1$@ вашей учетной записи (вы не можете войти в систему снова, и ваш ID не может быть использован повторно)."; +"screen_deactivate_account_description" = "Отключение вашей учетной записи %1$@ и означает следующее:"; +"screen_deactivate_account_description_bold_part" = "необратимо"; +"screen_deactivate_account_list_item_1" = "Ваша учётная запись будет %1$@ (вы не сможете войти в неё снова, и ваш ID не может быть использован повторно)."; "screen_deactivate_account_list_item_1_bold_part" = "Отключить навсегда"; -"screen_deactivate_account_list_item_2" = "Удалите вас из всех чатов."; -"screen_deactivate_account_list_item_3" = "Удалите данные своей учетной записи с нашего сервера идентификации."; +"screen_deactivate_account_list_item_2" = "Вы будете удалены из всех чатов."; +"screen_deactivate_account_list_item_3" = "Данные вашей учётной записи будут удалены с нашего сервера идентификации."; "screen_deactivate_account_list_item_4" = "Ваши сообщения по-прежнему будут видны зарегистрированным пользователям, но не будут доступны новым или незарегистрированным пользователям, если вы решите удалить их."; "screen_deactivate_account_title" = "Отключить учётную запись"; "screen_edit_poll_delete_confirmation" = "Вы уверены, что хотите удалить этот опрос?"; @@ -500,10 +513,10 @@ "screen_encryption_reset_footer" = "Сбрасывайте данные только в том случае, если у вас нет доступа к другому устройству, на котором выполнен вход, и вы потеряли ключ восстановления."; "screen_encryption_reset_title" = "Сбросьте ключи подтверждения, если вы не можете подтвердить свою личность другим способом."; "screen_identity_confirmation_cannot_confirm" = "Не можете подтвердить?"; -"screen_identity_confirmation_create_new_recovery_key" = "Создайте новый ключ восстановления"; +"screen_identity_confirmation_create_new_recovery_key" = "Создайте новый ключ восстановления"; "screen_identity_confirmation_subtitle" = "Подтвердите это устройство, чтобы настроить безопасный обмен сообщениями."; "screen_identity_confirmation_title" = "Подтвердите, что это вы"; -"screen_identity_confirmation_use_another_device" = "Используйте другое устройство"; +"screen_identity_confirmation_use_another_device" = "Использовать другое устройство"; "screen_identity_confirmation_use_recovery_key" = "Используйте recovery key"; "screen_identity_confirmed_subtitle" = "Теперь вы можете безопасно читать и отправлять сообщения, и все, с кем вы общаетесь в чате, также могут доверять этому устройству."; "screen_identity_confirmed_title" = "Устройство проверено"; @@ -529,7 +542,7 @@ "screen_key_backup_disable_description_point_1" = "Нет зашифрованной истории сообщений на новых устройствах"; "screen_key_backup_disable_description_point_2" = "Вы потеряете доступ к зашифрованным сообщениям, если выйдете из %1$@ везде"; "screen_key_backup_disable_title" = "Вы действительно хотите отключить резервное копирование?"; -"screen_login_error_deactivated_account" = "Данная учетная запись была деактивирована."; +"screen_login_error_deactivated_account" = "Данная учётная запись была отключена."; "screen_login_error_invalid_credentials" = "Неверное имя пользователя и/или пароль"; "screen_login_error_invalid_user_id" = "Это не корректный идентификатор пользователя. Ожидаемый формат: '@user:homeserver.org'"; "screen_login_error_refresh_tokens" = "Этот сервер настроен на использование токенов обновления. Они не поддерживаются при использовании входа на основе пароля."; @@ -620,29 +633,28 @@ "screen_qr_code_login_verify_code_subtitle" = "Поставщик учетной записи может запросить следующий код для подтверждения входа."; "screen_qr_code_login_verify_code_title" = "Ваш код подтверждения"; "screen_recovery_key_change_description" = "Получите новый ключ восстановления, если вы потеряли существующий. После смены ключа восстановления старый ключ больше не будет работать."; -"screen_recovery_key_change_generate_key" = "Создать новый ключ восстановления"; -"screen_recovery_key_change_generate_key_description" = "Убедитесь, что вы можете хранить ключ восстановления в безопасном месте"; -"screen_recovery_key_change_success" = "Ключ восстановления изменен"; +"screen_recovery_key_change_generate_key" = "Создать новый ключ восстановления"; +"screen_recovery_key_change_success" = "Ключ восстановления изменен"; "screen_recovery_key_change_title" = "Изменить ключ восстановления?"; -"screen_recovery_key_confirm_create_new_recovery_key" = "Создать новый ключ восстановления"; +"screen_recovery_key_confirm_create_new_recovery_key" = "Создать новый ключ восстановления"; "screen_recovery_key_confirm_description" = "Убедитесь, что никто не видит этот экран!"; "screen_recovery_key_confirm_error_content" = "Пожалуйста, попробуйте еще раз, чтобы подтвердить доступ к резервной копии чата."; -"screen_recovery_key_confirm_error_title" = "Неверный ключ восстановления"; +"screen_recovery_key_confirm_error_title" = "Неверный ключ восстановления"; "screen_recovery_key_confirm_key_description" = "Если у вас есть пароль для восстановления или секретный пароль/ключ, это тоже сработает."; "screen_recovery_key_confirm_key_placeholder" = "Вход…"; "screen_recovery_key_confirm_lost_recovery_key" = "Потеряли ключ восстановления?"; -"screen_recovery_key_confirm_success" = "Ключ восстановления подтвержден"; -"screen_recovery_key_copied_to_clipboard" = "Ключ восстановления скопирован"; +"screen_recovery_key_confirm_success" = "Ключ восстановления подтвержден"; +"screen_recovery_key_copied_to_clipboard" = "Ключ восстановления скопирован"; "screen_recovery_key_generating_key" = "Генерация…"; -"screen_recovery_key_save_action" = "Сохранить ключ восстановления"; -"screen_recovery_key_save_description" = "Запишите ключ восстановления в безопасном месте или сохраните его в менеджере паролей."; +"screen_recovery_key_save_action" = "Сохранить ключ восстановления"; +"screen_recovery_key_save_description" = "Запишите данный ключ восстановления в безопасном месте, например в диспетчере паролей, зашифрованной заметке или физическом сейфе."; "screen_recovery_key_save_key_description" = "Нажмите, чтобы скопировать ключ восстановления"; -"screen_recovery_key_save_title" = "Сохраните ключ восстановления"; +"screen_recovery_key_save_title" = "Сохраните ключ восстановления"; "screen_recovery_key_setup_confirmation_description" = "После этого шага вы не сможете получить доступ к новому ключу восстановления."; "screen_recovery_key_setup_confirmation_title" = "Вы сохранили ключ восстановления?"; "screen_recovery_key_setup_description" = "Резервная копия чата защищена ключом восстановления. Если после настройки вам понадобится новый ключ восстановления, вы можете создать его заново, выбрав «Изменить ключ восстановления»."; -"screen_recovery_key_setup_generate_key" = "Создайте ключ восстановления"; -"screen_recovery_key_setup_generate_key_description" = "Убедитесь, что вы можете хранить ключ восстановления в безопасном месте"; +"screen_recovery_key_setup_generate_key" = "Создайте ключ восстановления"; +"screen_recovery_key_setup_generate_key_description" = "Не сообщайте эту информацию никому!"; "screen_recovery_key_setup_success" = "Настройка восстановления выполнена успешно"; "screen_recovery_key_setup_title" = "Настроить восстановление"; "screen_report_content_block_user_hint" = "Отметьте, хотите ли вы скрыть все текущие и будущие сообщения от этого пользователя"; @@ -751,7 +763,7 @@ "screen_room_notification_settings_error_setting_mode" = "Не удалось настроить режим, попробуйте еще раз."; "screen_room_notification_settings_mentions_only_disclaimer" = "Ваш домашний сервер не поддерживает эту опцию в зашифрованных комнатах, вы не будете получать уведомления в этой комнате."; "screen_room_notification_settings_mode_all_messages" = "О всех сообщениях"; -"screen_room_notification_settings_room_custom_settings_title" = "В этой комнате уведомить меня о"; +"screen_room_notification_settings_room_custom_settings_title" = "В этой комнате уведомлять меня"; "screen_room_retry_send_menu_send_again_action" = "Отправить снова"; "screen_room_retry_send_menu_title" = "Не удалось отправить ваше сообщение"; "screen_room_roles_and_permissions_admins" = "Администраторы"; @@ -801,7 +813,7 @@ "screen_roomlist_filter_unreads_empty_state_title" = "Поздравляем!\nУ вас нет непрочитанных сообщений!"; "screen_roomlist_main_space_title" = "Все чаты"; "screen_roomlist_mark_as_read" = "Пометить как прочитанное"; -"screen_roomlist_mark_as_unread" = "Пометить как непрочитанное"; +"screen_roomlist_mark_as_unread" = "Отметить как непрочитанное"; "screen_roomlist_room_directory_button_title" = "Просмотреть все комнаты"; "screen_server_confirmation_message_login_element_dot_io" = "Частный сервер для сотрудников Element."; "screen_server_confirmation_message_login_matrix_dot_org" = "Matrix — это открытая сеть для безопасной децентрализованной связи."; @@ -814,22 +826,26 @@ "screen_session_verification_compare_numbers_subtitle" = "Убедитесь, что приведенные ниже числа совпадают с цифрами, показанными в другом сеансе."; "screen_session_verification_compare_numbers_title" = "Сравните числа"; "screen_session_verification_complete_subtitle" = "Ваш новый сеанс подтвержден. У него есть доступ к вашим зашифрованным сообщениям, и другие пользователи увидят его как доверенное."; -"screen_session_verification_enter_recovery_key" = "Введите ключ восстановления"; -"screen_session_verification_failed_subtitle" = "Запрос был отклонен, так как время ожидания запроса истекло, либо произошла ошибка при проверке."; +"screen_session_verification_enter_recovery_key" = "Введите ключ восстановления"; +"screen_session_verification_failed_subtitle" = "Время ожидания подтверждения истекло, запрос был отклонён, или при подтверждении произошло несоответствие."; "screen_session_verification_open_existing_session_subtitle" = "Чтобы получить доступ к зашифрованной истории сообщений, докажите, что это вы."; "screen_session_verification_open_existing_session_title" = "Открыть существующий сеанс"; -"screen_session_verification_positive_button_canceled" = "Повторить проверку"; +"screen_session_verification_positive_button_canceled" = "Повторить подтверждение"; "screen_session_verification_positive_button_initial" = "Я готов"; -"screen_session_verification_positive_button_verifying_ongoing" = "Ожидание соответствия"; +"screen_session_verification_positive_button_verifying_ongoing" = "Ожидание соответствия…"; "screen_session_verification_ready_subtitle" = "Сравните уникальный набор эмодзи."; "screen_session_verification_request_accepted_subtitle" = "Сравните уникальные смайлики, убедившись, что они расположены в том же порядке."; "screen_session_verification_request_details_timestamp" = "Вход выполнен"; -"screen_session_verification_request_footer" = "Продолжайте только в том случае, если вы инициировали эту проверку."; +"screen_session_verification_request_failure_subtitle" = "Время ожидания подтверждения истекло, запрос был отклонён, или при подтверждении произошло несоответствие."; +"screen_session_verification_request_failure_title" = "Сбой проверки"; +"screen_session_verification_request_footer" = "Продолжайте только если вы ожидали данное подтверждение."; "screen_session_verification_request_subtitle" = "Чтобы сохранить историю сообщений в безопасности, проверьте другое устройство."; -"screen_session_verification_request_title" = "Запрос на верификация"; +"screen_session_verification_request_success_subtitle" = "Теперь вы можете безопасно читать или отправлять сообщения на другом устройстве."; +"screen_session_verification_request_success_title" = "Устройство проверено"; +"screen_session_verification_request_title" = "Запрошено подтверждение"; "screen_session_verification_they_dont_match" = "Они не совпадают"; "screen_session_verification_they_match" = "Они совпадают"; -"screen_session_verification_waiting_to_accept_subtitle" = "Для продолжения работы примите запрос на запуск процесса проверки в другом сеансе."; +"screen_session_verification_waiting_to_accept_subtitle" = "Чтобы продолжить, примите запрос на запуск процесса подтверждения в другом сеансе."; "screen_session_verification_waiting_to_accept_title" = "Ожидание принятия запроса"; "screen_share_location_title" = "Поделиться местоположением"; "screen_share_my_location_action" = "Поделиться моим местоположением"; @@ -860,7 +876,7 @@ "screen_welcome_button" = "Поехали!"; "screen_welcome_subtitle" = "Вот что вам необходимо знать:"; "screen_welcome_title" = "Добро пожаловать в %1$@!"; -"session_verification_banner_message" = "Похоже, вы используете новое устройство. Чтобы получить доступ к зашифрованным сообщениям пройдите верификацию с другим устройством."; +"session_verification_banner_message" = "Похоже, вы используете новое устройство. Чтобы получить доступ к зашифрованным сообщениям пройдите подтверждение с другим устройством."; "session_verification_banner_title" = "Подтвердите, что это вы"; "settings_rageshake" = "Встряхните"; "settings_rageshake_detection_threshold" = "Порог обнаружения"; @@ -995,7 +1011,7 @@ "screen_blocked_users_unblock_alert_description" = "Вы снова сможете увидеть все сообщения."; "screen_blocked_users_unblock_alert_title" = "Разблокировать пользователя"; "screen_bug_report_rash_logs_alert_title" = "При последнем использовании %1$@ произошел сбой. Хотите поделиться отчетом о сбое?"; -"screen_chat_backup_recovery_action_confirm" = "Введите ключ восстановления"; +"screen_chat_backup_recovery_action_confirm" = "Введите ключ восстановления"; "screen_create_poll_cancel_confirmation_content_ios" = "Ваши изменения не будут сохранены"; "screen_create_room_add_people_title" = "Пригласить в комнату"; "screen_create_room_room_name_label" = "Название комнаты"; @@ -1008,11 +1024,12 @@ "screen_dm_details_unblock_user" = "Разблокировать пользователя"; "screen_edit_poll_delete_confirmation_title" = "Удалить опрос"; "screen_edit_poll_title" = "Редактировать опрос"; -"screen_identity_use_another_device" = "Используйте другое устройство"; +"screen_identity_use_another_device" = "Использовать другое устройство"; "screen_login_subtitle" = "Matrix — это открытая сеть для безопасной децентрализованной связи."; "screen_notification_settings_mentions_section_title" = "Упоминания"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Повторить попытку"; -"screen_recovery_key_confirm_title" = "Введите ключ восстановления"; +"screen_recovery_key_change_generate_key_description" = "Не сообщайте эту информацию никому!"; +"screen_recovery_key_confirm_title" = "Введите ключ восстановления"; "screen_report_content_block_user" = "Заблокировать пользователя"; "screen_reset_encryption_password_placeholder" = "Вход…"; "screen_room_attachment_source_camera_photo" = "Сделать фото"; @@ -1035,7 +1052,7 @@ "screen_room_error_failed_processing_media" = "Не удалось обработать медиафайл для загрузки, попробуйте еще раз."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Удалить и заблокировать участника"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Только упоминания и ключевые слова"; -"screen_roomlist_filter_people" = "Люди"; +"screen_roomlist_filter_people" = "Пользователи"; "screen_server_confirmation_change_server" = "Сменить поставщика учетной записи"; "screen_signout_confirmation_dialog_submit" = "Выйти"; "screen_signout_confirmation_dialog_title" = "Выйти"; diff --git a/ElementX/Resources/Localizations/sk.lproj/Localizable.strings b/ElementX/Resources/Localizations/sk.lproj/Localizable.strings index 0b62d918e3..9ea9cef956 100644 --- a/ElementX/Resources/Localizations/sk.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/sk.lproj/Localizable.strings @@ -54,7 +54,7 @@ "action_forgot_password" = "Zabudnuté heslo?"; "action_forward" = "Preposlať"; "action_go_back" = "Ísť späť"; -"action_ignore" = "Ignore"; +"action_ignore" = "Ignorovať"; "action_invite" = "Pozvať"; "action_invite_friends" = "Pozvať ľudí"; "action_invite_friends_to_app" = "Pozvať ľudí do %1$@"; @@ -134,11 +134,12 @@ "common_dark" = "Tmavý"; "common_decryption_error" = "Chyba dešifrovania"; "common_developer_options" = "Možnosti pre vývojárov"; -"common_device_id" = "Device ID"; +"common_device_id" = "ID zariadenia"; "common_direct_chat" = "Priama konverzácia"; "common_edited_suffix" = "(upravené)"; "common_editing" = "Upravuje sa"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Šifrovanie"; "common_encryption_enabled" = "Šifrovanie zapnuté"; "common_enter_your_pin" = "Zadajte svoj PIN"; "common_error" = "Chyba"; @@ -149,6 +150,7 @@ "common_favourited" = "Obľúbené"; "common_file" = "Súbor"; "common_forward_message" = "Preposlať správu"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Obrázok"; "common_in_reply_to" = "V odpovedi na %1$@"; @@ -228,8 +230,8 @@ "common_username" = "Používateľské meno"; "common_verification_cancelled" = "Overovanie zrušené"; "common_verification_complete" = "Overovanie je dokončené"; -"common_verification_failed" = "Verification failed"; -"common_verified" = "Verified"; +"common_verification_failed" = "Overenie zlyhalo"; +"common_verified" = "Overené"; "common_verify_device" = "Overiť zariadenie"; "common_video" = "Video"; "common_voice_message" = "Hlasová správa"; @@ -241,11 +243,13 @@ "common.pinned" = "Pripnuté"; "common.send_to" = "Odoslať"; "common.you" = "Vy"; +"common_unable_to_decrypt_insecure_device" = "Odoslané z nezabezpečeného zariadenia"; +"common_unable_to_decrypt_verification_violation" = "Overená totožnosť odosielateľa sa zmenila"; "confirm_recovery_key_banner_message" = "Vaša záloha konverzácie nie je momentálne synchronizovaná. Na zachovanie prístupu k zálohe konverzácie musíte potvrdiť svoj kľúč na obnovu."; "confirm_recovery_key_banner_title" = "Potvrďte svoj kľúč na obnovenie"; "crash_detection_dialog_content" = "%1$@ zlyhal pri poslednom použití. Chcete zdieľať správu o páde s našim tímom?"; "crypto_identity_change_pin_violation" = "Zdá sa, že totožnosť používateľa %1$@ sa zmenila.%2$@"; -"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation_new" = "Zdá sa, že identita %2$@ používateľa %1$@ sa zmenila. %3$@"; "crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Aby aplikácia mohla používať fotoaparát, udeľte povolenie v systémových nastaveniach."; "dialog_permission_generic" = "Udeľte prosím povolenie v systémových nastaveniach."; @@ -340,18 +344,21 @@ "screen_advanced_settings_element_call_base_url" = "Vlastná Element Call základná URL adresa"; "screen_advanced_settings_element_call_base_url_description" = "Nastaviť vlastnú základnú URL adresu pre Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Neplatná adresa URL, uistite sa, že ste uviedli protokol (http/https) a správnu adresu."; -"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"; -"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; -"screen_create_room_access_section_knocking_option_title" = "Ask to join"; -"screen_join_room_cancel_knock_action" = "Cancel request"; -"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; -"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; -"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; -"screen_join_room_knock_message_description" = "Message (optional)"; -"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; -"screen_join_room_knock_sent_title" = "Request to join sent"; +"screen_create_room_room_address_section_footer" = "Aby bola táto miestnosť viditeľná v adresári verejných miestností, budete potrebovať adresu miestnosti."; +"screen_create_room_room_address_section_title" = "Adresa miestnosti"; +"screen_create_room_room_visibility_section_title" = "Viditeľnosť miestnosti"; +"screen_create_room_access_section_anyone_option_description" = "Do tejto miestnosti sa môže pripojiť ktokoľvek"; +"screen_create_room_access_section_anyone_option_title" = "Ktokoľvek"; +"screen_create_room_access_section_header" = "Prístup do miestnosti"; +"screen_create_room_access_section_knocking_option_description" = "Ktokoľvek môže požiadať o pripojenie sa k miestnosti, ale administrátor alebo moderátor bude musieť žiadosť schváliť"; +"screen_create_room_access_section_knocking_option_title" = "Požiadať o pripojenie"; +"screen_join_room_cancel_knock_action" = "Zrušiť žiadosť"; +"screen_join_room_cancel_knock_alert_confirmation" = "Áno, zrušiť"; +"screen_join_room_cancel_knock_alert_description" = "Ste si istí, že chcete zrušiť svoju žiadosť o vstup do tejto miestnosti?"; +"screen_join_room_cancel_knock_alert_title" = "Zrušiť žiadosť o pripojenie"; +"screen_join_room_knock_message_description" = "Správa (voliteľné)"; +"screen_join_room_knock_sent_description" = "Ak bude vaša žiadosť prijatá, dostanete pozvánku na vstup do miestnosti."; +"screen_join_room_knock_sent_title" = "Žiadosť o pripojenie bola odoslaná"; "screen_pinned_timeline_empty_state_description" = "Stlačte správu a vyberte možnosť „%1$@“, ktorú chcete zahrnúť sem."; "screen_pinned_timeline_empty_state_headline" = "Pripnite dôležité správy, aby sa dali ľahko nájsť"; "screen_reset_encryption_password_error" = "Nastala neznáma chyba. Skontrolujte, či je heslo vášho účtu správne a skúste to znova."; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Načítava sa správa..."; "screen_room_pinned_banner_view_all_button_title" = "Zobraziť všetko"; "screen_room_details_pinned_events_row_title" = "Pripnuté správy"; +"screen_roomlist_knock_event_sent_description" = "Žiadosť o vstup odoslaná"; "screen_timeline_item_menu_send_failure_changed_identity" = "Správa nebola odoslaná, pretože sa zmenila overená totožnosť používateľa %1$@."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Správa nebola odoslaná, pretože %1$@ neoveril/a všetky zariadenia."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Správa nebola odoslaná, pretože ste neoverili jedno alebo viac svojich zariadení."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Chystáte sa vytvoriť účet na %@"; "screen_advanced_settings_developer_mode" = "Vývojársky režim"; "screen_advanced_settings_developer_mode_description" = "Umožniť prístup k možnostiam a funkciám pre vývojárov."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Vypnite rozšírený textový editor na ručné písanie Markdown."; "screen_advanced_settings_send_read_receipts" = "Potvrdenia o prečítaní"; "screen_advanced_settings_send_read_receipts_description" = "Ak je táto funkcia vypnutá, vaše potvrdenia o prečítaní sa nebudú nikomu odosielať. Stále budete dostávať potvrdenia o prečítaní od ostatných používateľov."; @@ -446,9 +456,12 @@ "screen_change_server_title" = "Vyberte svoj server"; "screen_chat_backup_key_backup_action_disable" = "Vypnúť zálohovanie"; "screen_chat_backup_key_backup_action_enable" = "Zapnúť zálohovanie"; -"screen_chat_backup_key_backup_description" = "Zálohovanie zaisťuje, že nestratíte históriu správ. %1$@."; -"screen_chat_backup_key_backup_title" = "Zálohovanie"; +"screen_chat_backup_key_backup_description" = "Uložte svoju kryptografickú identitu a kľúče správ bezpečne na server. To vám umožní zobraziť históriu správ na všetkých nových zariadeniach. %1$@."; +"screen_chat_backup_key_backup_title" = "Úložisko kľúčov"; +"screen_chat_backup_key_storage_toggle_description" = "Nahrať kľúče z tohto zariadenia"; +"screen_chat_backup_key_storage_toggle_title" = "Povoliť úložisko kľúčov"; "screen_chat_backup_recovery_action_change" = "Zmeniť kľúč na obnovenie"; +"screen_chat_backup_recovery_action_change_description" = "Obnovte svoju kryptografickú totožnosť a históriu správ pomocou kľúča na obnovenie, ak ste stratili všetky svoje existujúce zariadenia."; "screen_chat_backup_recovery_action_confirm_description" = "Vaša záloha konverzácie nie je momentálne synchronizovaná."; "screen_chat_backup_recovery_action_setup" = "Nastaviť obnovovanie"; "screen_chat_backup_recovery_action_setup_description" = "Získajte prístup k vašim šifrovaným správam aj keď stratíte všetky svoje zariadenia alebo sa odhlásite zo všetkých %1$@ zariadení."; @@ -470,10 +483,10 @@ "screen_create_poll_title" = "Vytvoriť anketu"; "screen_create_room_action_create_room" = "Nová miestnosť"; "screen_create_room_error_creating_room" = "Pri vytváraní miestnosti došlo k chybe"; -"screen_create_room_private_option_description" = "Správy v tejto miestnosti sú šifrované. Šifrovanie už potom nie je možné vypnúť."; -"screen_create_room_private_option_title" = "Súkromná miestnosť (len pre pozvaných)"; -"screen_create_room_public_option_description" = "Správy nie sú šifrované a môže si ich prečítať ktokoľvek. Šifrovanie môžete zapnúť neskôr."; -"screen_create_room_public_option_title" = "Verejná miestnosť (ktokoľvek)"; +"screen_create_room_private_option_description" = "Do tejto miestnosti majú prístup iba pozvaní ľudia. Všetky správy sú end-to-end šifrované."; +"screen_create_room_private_option_title" = "Súkromná miestnosť"; +"screen_create_room_public_option_description" = "Túto miestnosť môže nájsť ktokoľvek.\nMôžete to kedykoľvek zmeniť v nastaveniach miestnosti."; +"screen_create_room_public_option_title" = "Verejná miestnosť"; "screen_create_room_topic_label" = "Téma (voliteľné)"; "screen_deactivate_account_confirmation_dialog_content" = "Prosím potvrďte, že chcete deaktivovať svoj účet. Túto akciu nie je možné vrátiť späť."; "screen_deactivate_account_delete_all_messages" = "Vymazať všetky moje správy"; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Váš overovací kód"; "screen_recovery_key_change_description" = "Získajte nový kľúč na obnovenie, ak ste stratili svoj existujúci. Po zmene kľúča na obnovenie už starý kľúč nebude fungovať."; "screen_recovery_key_change_generate_key" = "Vygenerovať nový kľúč na obnovenie"; -"screen_recovery_key_change_generate_key_description" = "Uistite sa, že kľúč na obnovenie môžete uložiť niekde v bezpečí"; "screen_recovery_key_change_success" = "Kľúč na obnovenie bol zmenený"; "screen_recovery_key_change_title" = "Zmeniť kľúč na obnovenie?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Vytvoriť nový kľúč na obnovenie"; @@ -635,14 +647,14 @@ "screen_recovery_key_copied_to_clipboard" = "Skopírovaný kľúč na obnovenie"; "screen_recovery_key_generating_key" = "Generovanie..."; "screen_recovery_key_save_action" = "Uložiť kľúč na obnovenie"; -"screen_recovery_key_save_description" = "Zapíšte si kľúč na obnovenie na bezpečné miesto alebo ho uložte do správcu hesiel."; +"screen_recovery_key_save_description" = "Zapíšte si tento kľúč na obnovenie na bezpečné miesto, napríklad do správcu hesiel, šifrovanej poznámky alebo fyzického trezoru."; "screen_recovery_key_save_key_description" = "Ťuknutím skopírujte kľúč na obnovenie"; "screen_recovery_key_save_title" = "Uložte svoj kľúč na obnovenie"; "screen_recovery_key_setup_confirmation_description" = "Po tomto kroku nebudete mať prístup k novému kľúču na obnovenie."; "screen_recovery_key_setup_confirmation_title" = "Uložili ste kľúč na obnovenie?"; "screen_recovery_key_setup_description" = "Vaša záloha konverzácie je chránená kľúčom na obnovenie. Ak potrebujete nový kľúč na obnovenie, po nastavení si ho môžete znova vytvoriť výberom položky „Zmeniť kľúč na obnovenie“."; "screen_recovery_key_setup_generate_key" = "Vygenerujte si váš kľúč na obnovenie"; -"screen_recovery_key_setup_generate_key_description" = "Uistite sa, že kľúč na obnovenie môžete uložiť niekde v bezpečí"; +"screen_recovery_key_setup_generate_key_description" = "Nezdieľajte to s nikým!"; "screen_recovery_key_setup_success" = "Úspešné nastavenie obnovy"; "screen_recovery_key_setup_title" = "Nastaviť obnovenie"; "screen_report_content_block_user_hint" = "Označte, či chcete skryť všetky aktuálne a budúce správy od tohto používateľa"; @@ -716,8 +728,8 @@ "screen_room_member_details_unblock_alert_action" = "Odblokovať"; "screen_room_member_details_unblock_alert_description" = "Všetky správy od nich budete môcť opäť vidieť."; "screen_room_member_details_unblock_user" = "Odblokovať používateľa"; -"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; -"screen_room_member_details_verify_button_title" = "Verify %1$@"; +"screen_room_member_details_verify_button_subtitle" = "Použite webovú aplikáciu na overenie tohto používateľa."; +"screen_room_member_details_verify_button_title" = "Overiť %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Zakázať"; "screen_room_member_list_ban_member_confirmation_description" = "Nebudú sa môcť pripojiť k tejto miestnosti znova ani ak budú pozvaní."; "screen_room_member_list_ban_member_confirmation_title" = "Ste si istý, že chcete zakázať tohto člena?"; @@ -815,7 +827,7 @@ "screen_session_verification_compare_numbers_title" = "Porovnať čísla"; "screen_session_verification_complete_subtitle" = "Vaša nová relácia je teraz overená. Má prístup k vašim zašifrovaným správam a ostatní používatelia ju budú vidieť ako dôveryhodnú."; "screen_session_verification_enter_recovery_key" = "Zadajte kľúč na obnovenie"; -"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_failed_subtitle" = "Buď žiadosť vypršala, žiadosť bola zamietnutá, alebo došlo k nesúladu overovania."; "screen_session_verification_open_existing_session_subtitle" = "Dokážte, že ste to vy, aby ste získali prístup k histórii vašich zašifrovaných správ."; "screen_session_verification_open_existing_session_title" = "Otvoriť existujúcu reláciu"; "screen_session_verification_positive_button_canceled" = "Zopakovať overenie"; @@ -823,10 +835,14 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Čaká sa na zhodu"; "screen_session_verification_ready_subtitle" = "Porovnajte jedinečnú sadu emotikonov."; "screen_session_verification_request_accepted_subtitle" = "Porovnajte jedinečné emotikony a uistite sa, že sú zobrazené v rovnakom poradí."; -"screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; -"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; -"screen_session_verification_request_title" = "Verification requested"; +"screen_session_verification_request_details_timestamp" = "Prihlásený"; +"screen_session_verification_request_failure_subtitle" = "Buď žiadosť vypršala, žiadosť bola zamietnutá, alebo došlo k nesúladu overovania."; +"screen_session_verification_request_failure_title" = "Overenie zlyhalo"; +"screen_session_verification_request_footer" = "Pokračujte iba vtedy, ak ste toto overenie začali."; +"screen_session_verification_request_subtitle" = "Overte druhé zariadenie, aby bola vaša história správ zabezpečená."; +"screen_session_verification_request_success_subtitle" = "Teraz môžete bezpečne čítať alebo odosielať správy na svojom druhom zariadení."; +"screen_session_verification_request_success_title" = "Zariadenie overené"; +"screen_session_verification_request_title" = "Vyžadované overenie"; "screen_session_verification_they_dont_match" = "Nezhodujú sa"; "screen_session_verification_they_match" = "Zhodujú sa"; "screen_session_verification_waiting_to_accept_subtitle" = "Ak chcete pokračovať, prijmite žiadosť o spustenie procesu overenia vo vašej druhej relácii."; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix je otvorená sieť pre bezpečnú a decentralizovanú komunikáciu."; "screen_notification_settings_mentions_section_title" = "Zmienky"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Skúste to znova"; +"screen_recovery_key_change_generate_key_description" = "Nezdieľajte to s nikým!"; "screen_recovery_key_confirm_title" = "Potvrďte svoj kľúč na obnovenie"; "screen_report_content_block_user" = "Zablokovať používateľa"; "screen_reset_encryption_password_placeholder" = "Zadať..."; diff --git a/ElementX/Resources/Localizations/sv.lproj/Localizable.strings b/ElementX/Resources/Localizations/sv.lproj/Localizable.strings index 4486249e57..4154418ba1 100644 --- a/ElementX/Resources/Localizations/sv.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/sv.lproj/Localizable.strings @@ -112,9 +112,9 @@ "action_view_source" = "Visa källkod"; "action_yes" = "Ja"; "banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; -"banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; +"banner_migrate_to_native_sliding_sync_description" = "Din server stöder nu ett nytt, snabbare protokoll. Logga ut och logga in igen för att uppgradera nu. Om du gör detta nu hjälper du dig att undvika en tvingad utloggning när det gamla protokollet tas bort senare."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; -"banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; +"banner_migrate_to_native_sliding_sync_title" = "Uppgradering tillgänglig"; "banner.set_up_recovery.content" = "Skapa en ny återställningsnyckel som kan användas för att återställa din krypterade meddelandehistorik om du förlorar åtkomst till dina enheter."; "banner.set_up_recovery.title" = "Ställ in återställning"; "common_about" = "Om"; @@ -139,6 +139,7 @@ "common_edited_suffix" = "(redigerad)"; "common_editing" = "Redigerar"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "Kryptering aktiverad"; "common_enter_your_pin" = "Ange din PIN-kod"; "common_error" = "Fel"; @@ -149,6 +150,7 @@ "common_favourited" = "Favoriter"; "common_file" = "Fil"; "common_forward_message" = "Vidarebefordra meddelande"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Bild"; "common_in_reply_to" = "Som svar på %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Fäst"; "common.send_to" = "Skicka till"; "common.you" = "You"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Din chattsäkerhetskopia är för närvarande inte synkroniserad. Du måste ange din återställningsnyckel för att behålla åtkomsten till din chattsäkerhetskopia."; "confirm_recovery_key_banner_title" = "Ange din återställningsnyckel"; "crash_detection_dialog_content" = "%1$@ kraschade senast den användes. Vill du dela en kraschrapport med oss?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "Anpassad bas-URL för Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Ange en anpassad bas-URL för Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Ogiltig URL, se till att du inkluderar protokollet (http/https) och rätt adress."; +"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"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Laddar meddelande …"; "screen_room_pinned_banner_view_all_button_title" = "Visa alla"; "screen_room_details_pinned_events_row_title" = "Fästa meddelanden"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Du är på väg att skapa ett konto på %@"; "screen_advanced_settings_developer_mode" = "Utvecklarläge"; "screen_advanced_settings_developer_mode_description" = "Aktivera för att ha tillgång till funktionalitet för utvecklare."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Inaktivera rik-text-redigeraren för att skriva Markdown manuellt."; "screen_advanced_settings_send_read_receipts" = "Läskvitton"; "screen_advanced_settings_send_read_receipts_description" = "Om det är avstängt kommer dina läskvitton inte att skickas till någon. Du kommer fortfarande att få läskvitton från andra användare."; @@ -447,8 +457,11 @@ "screen_chat_backup_key_backup_action_disable" = "Stäng av säkerhetskopiering"; "screen_chat_backup_key_backup_action_enable" = "Slå på säkerhetskopiering"; "screen_chat_backup_key_backup_description" = "Säkerhetskopior ser till att du inte blir av med din meddelandehistorik. %1$@."; -"screen_chat_backup_key_backup_title" = "Säkerhetskopia"; +"screen_chat_backup_key_backup_title" = "Nyckellagring"; +"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" = "Byt återställningsnyckel"; +"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" = "Din chattsäkerhetskopia är för närvarande osynkroniserad."; "screen_chat_backup_recovery_action_setup" = "Ställ in återställning"; "screen_chat_backup_recovery_action_setup_description" = "Få tillgång till dina krypterade meddelanden om du tappar bort alla dina enheter eller blir utloggad ur %1$@ överallt."; @@ -470,10 +483,10 @@ "screen_create_poll_title" = "Skapa omröstning"; "screen_create_room_action_create_room" = "Nytt rum"; "screen_create_room_error_creating_room" = "Ett fel uppstod när rummet skapades"; -"screen_create_room_private_option_description" = "Meddelanden i det här rummet är krypterade. Kryptering kan inte inaktiveras efteråt."; -"screen_create_room_private_option_title" = "Privat rum (endast inbjudan)"; -"screen_create_room_public_option_description" = "Meddelanden är inte krypterade och vem som helst kan läsa dem. Du kan aktivera kryptering vid ett senare tillfälle."; -"screen_create_room_public_option_title" = "Offentligt rum (vem som helst)"; +"screen_create_room_private_option_description" = "Endast inbjudna personer har tillgång till detta rum. Alla meddelanden är totalsträckskrypterade."; +"screen_create_room_private_option_title" = "Privat rum"; +"screen_create_room_public_option_description" = "Vem som helst kan hitta det här rummet.\nDu kan ändra detta när som helst i rumsinställningarna."; +"screen_create_room_public_option_title" = "Offentligt rum"; "screen_create_room_topic_label" = "Ämne (valfritt)"; "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"; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Din verifieringskod"; "screen_recovery_key_change_description" = "Få en ny återställningsnyckel om du har tappat bort din befintliga. När du har bytt din återställningsnyckel fungerar din gamla inte längre."; "screen_recovery_key_change_generate_key" = "Generera en ny återställningsnyckel"; -"screen_recovery_key_change_generate_key_description" = "Se till att du kan lagra din återställningsnyckel någonstans säkert"; "screen_recovery_key_change_success" = "Återställningsnyckel ändrad"; "screen_recovery_key_change_title" = "Byt återställningsnyckel?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Skapa ny återställningsnyckel"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Jämför en unik uppsättning emojis."; "screen_session_verification_request_accepted_subtitle" = "Jämför de unika emojierna och se till att de visas i samma ordning."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "De matchar inte"; "screen_session_verification_they_match" = "De matchar"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix är ett öppet nätverk för säker, decentraliserad kommunikation."; "screen_notification_settings_mentions_section_title" = "Omnämnanden"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Försök igen"; +"screen_recovery_key_change_generate_key_description" = "Se till att du kan lagra din återställningsnyckel någonstans säkert"; "screen_recovery_key_confirm_title" = "Ange din återställningsnyckel"; "screen_report_content_block_user" = "Blockera användare"; "screen_reset_encryption_password_placeholder" = "Ange …"; diff --git a/ElementX/Resources/Localizations/uk.lproj/Localizable.strings b/ElementX/Resources/Localizations/uk.lproj/Localizable.strings index 0f0b4fbd20..4fa6bb8ef6 100644 --- a/ElementX/Resources/Localizations/uk.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/uk.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(відредаговано)"; "common_editing" = "Редагування"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "Шифрування ввімкнено"; "common_enter_your_pin" = "Введіть свій PIN-код"; "common_error" = "Помилка"; @@ -149,6 +150,7 @@ "common_favourited" = "Вибране"; "common_file" = "Файл"; "common_forward_message" = "Переслати повідомлення"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "Зображення"; "common_in_reply_to" = "У відповідь на %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Pinned"; "common.send_to" = "Надіслати до"; "common.you" = "You"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Ваша резервна копія чату наразі не синхронізована. Вам потрібно підтвердити ключ відновлення, щоб зберегти доступ до резервної копії чату."; "confirm_recovery_key_banner_title" = "Підтвердіть ключ відновлення"; "crash_detection_dialog_content" = "%1$@ аварійно завершив роботу під час останнього використання. Бажаєте поділитися з нами звітом про збій?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "Користувацька URL-адреса Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Встановіть URL-адресу для Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Неправильна URL-адреса, будь ласка, переконайтеся, що ви вказали протокол (http/https) та правильну адресу."; +"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"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Loading message…"; "screen_room_pinned_banner_view_all_button_title" = "Переглянути всі"; "screen_room_details_pinned_events_row_title" = "Pinned messages"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Ви збираєтеся створити обліковий запис на %@"; "screen_advanced_settings_developer_mode" = "Режим розробника"; "screen_advanced_settings_developer_mode_description" = "Увімкніть доступ до функцій і можливостей для розробників."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Вимкніть редактор розширеного тексту, щоб вводити Markdown вручну."; "screen_advanced_settings_send_read_receipts" = "Читати журнали"; "screen_advanced_settings_send_read_receipts_description" = "Якщо вимкнено, ваші сповіщення про прочитання нікому не надсилатимуться. Ви все одно отримуватимете сповіщення про прочитання від інших користувачів."; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "Увімкнути резервне копіювання"; "screen_chat_backup_key_backup_description" = "Резервне копіювання гарантує, що ви не втратите історію повідомлень. %1$@."; "screen_chat_backup_key_backup_title" = "Резервне копіювання"; +"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" = "Змінити ключ відновлення"; +"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" = "Ваша резервна копія чату наразі не синхронізована."; "screen_chat_backup_recovery_action_setup" = "Налаштувати відновлення"; "screen_chat_backup_recovery_action_setup_description" = "Отримайте доступ до своїх зашифрованих повідомлень, якщо ви втратите всі свої пристрої або вийшли з %1$@ системи."; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Ваш код підтвердження"; "screen_recovery_key_change_description" = "Отримайте новий ключ відновлення, якщо ви втратили існуючий ключ. Після зміни ключа відновлення ваш старий більше не буде працювати."; "screen_recovery_key_change_generate_key" = "Згенерувати новий ключ відновлення"; -"screen_recovery_key_change_generate_key_description" = "Переконайтеся, що ви можете зберігати ключ відновлення в безпечному місці"; "screen_recovery_key_change_success" = "Ключ відновлення змінено"; "screen_recovery_key_change_title" = "Змінити ключ відновлення?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Створити новий ключ відновлення"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Порівняйте унікальний набір емоджи."; "screen_session_verification_request_accepted_subtitle" = "Порівняйте унікальні емодзі, переконавшись, що вони відображаються в однаковому порядку."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Вони не збігаються"; "screen_session_verification_they_match" = "Вони збігаються"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix — це відкрита мережа для безпечної, децентралізованої комунікації."; "screen_notification_settings_mentions_section_title" = "Згадки"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Спробуйте ще раз"; +"screen_recovery_key_change_generate_key_description" = "Переконайтеся, що ви можете зберігати ключ відновлення в безпечному місці"; "screen_recovery_key_confirm_title" = "Підтвердіть ключ відновлення"; "screen_report_content_block_user" = "Заблокувати користувача"; "screen_reset_encryption_password_placeholder" = "Ввести..."; diff --git a/ElementX/Resources/Localizations/uz.lproj/Localizable.strings b/ElementX/Resources/Localizations/uz.lproj/Localizable.strings index 517aa829f1..07780a5617 100644 --- a/ElementX/Resources/Localizations/uz.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/uz.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(tahrirlangan)"; "common_editing" = "Tahrirlash"; "common_emote" = "*%1$@%2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "Shifrlash yoqilgan"; "common_enter_your_pin" = "Enter your PIN"; "common_error" = "Xato"; @@ -149,6 +150,7 @@ "common_favourited" = "Favourited"; "common_file" = "Fayl"; "common_forward_message" = "Xabarni yo'naltirish"; +"common_frequently_used" = "Frequently used"; "common_gif" = ""; "common_image" = "Surat"; "common_in_reply_to" = "%1$@ga Javob bering"; @@ -241,6 +243,8 @@ "common.pinned" = "Pinned"; "common.send_to" = "Send to"; "common.you" = "You"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup."; "confirm_recovery_key_banner_title" = "Enter your recovery key"; "crash_detection_dialog_content" = "%1$@oxirgi marta ishlatilganda qulab tushdi. Biz bilan nosozlik hisobotini baham ko'rmoqchimisiz?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "Maxsus element qo‘ng‘iroqlar bazasi URL manzili"; "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"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Loading message…"; "screen_room_pinned_banner_view_all_button_title" = "View All"; "screen_room_details_pinned_events_row_title" = "Pinned messages"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "Siz %@da hisob yaratmoqchisiz"; "screen_advanced_settings_developer_mode" = "Dasturchi rejimi"; "screen_advanced_settings_developer_mode_description" = "Ishlab chiquvchilar uchun xususiyatlar va funksiyalarga kirishni yoqing."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Boy matn muharriri o'chiring Markdown bilan qo'lda yozish uchun"; "screen_advanced_settings_send_read_receipts" = "Read receipts"; "screen_advanced_settings_send_read_receipts_description" = "If turned off, your read receipts won't be sent to anyone. You will still receive read receipts from other users."; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "Zaxiralashni yoqing"; "screen_chat_backup_key_backup_description" = "Zaxiralash xabarlar tarixini yo'qotmaslikni ta'minlaydi.%1$@."; "screen_chat_backup_key_backup_title" = "Zaxira"; +"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" = "Qayta tiklash kalitini o'zgartiring"; +"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" = "Sizning chat zaxirangiz hozirda sinxronlashtirilmagan."; "screen_chat_backup_recovery_action_setup" = "Qayta tiklashni sozlang"; "screen_chat_backup_recovery_action_setup_description" = "Agar barcha qurilmalaringizni yo‘qotib qo‘ysangiz yoki tizimdan chiqqan bo‘lsangiz, shifrlangan xabarlaringizga ruxsat oling%1$@ hamma joyda."; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "Your verification code"; "screen_recovery_key_change_description" = "Mavjud kalitingizni yo'qotgan bo'lsangiz, yangi tiklash kalitini oling. Qayta tiklash kalitini almashtirganingizdan so'ng, eski kalitingiz ishlamaydi."; "screen_recovery_key_change_generate_key" = "Yangi tiklash kalitini yarating"; -"screen_recovery_key_change_generate_key_description" = "Qayta tiklash kalitingizni xavfsiz joyda saqlashingiz mumkinligiga ishonch hosil qiling"; "screen_recovery_key_change_success" = "Qayta tiklash kaliti oʻzgartirildi"; "screen_recovery_key_change_title" = "Qayta tiklash kaliti almashtirilsinmi?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Create new recovery key"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "Compare a unique set of emojis."; "screen_session_verification_request_accepted_subtitle" = "Noyob emojilarni solishtiring, ular bir xil tartibda paydo bo'lishiga ishonch hosil qiling."; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "Ular mos kelmaydi"; "screen_session_verification_they_match" = "Ular mos keladi"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix xavfsiz, markazlashmagan aloqa uchun ochiq tarmoqdir."; "screen_notification_settings_mentions_section_title" = "Eslatmalar"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Try again"; +"screen_recovery_key_change_generate_key_description" = "Qayta tiklash kalitingizni xavfsiz joyda saqlashingiz mumkinligiga ishonch hosil qiling"; "screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Foydalanuvchini bloklash"; "screen_reset_encryption_password_placeholder" = "Kirish…"; diff --git a/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings b/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings index 7da78d194c..1d0e92c33c 100644 --- a/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(已编辑)"; "common_editing" = "编辑中"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "已启用加密"; "common_enter_your_pin" = "输入 PIN 码"; "common_error" = "错误"; @@ -149,6 +150,7 @@ "common_favourited" = "已收藏"; "common_file" = "文件"; "common_forward_message" = "转发消息"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "图片"; "common_in_reply_to" = "回复 %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Pinned"; "common.send_to" = "发送至"; "common.you" = "You"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "聊天备份目前不同步,需要输入恢复密钥才能访问聊天备份。"; "confirm_recovery_key_banner_title" = "输入恢复密钥"; "crash_detection_dialog_content" = "%1$@ 上次使用时崩溃了。想和我们分享崩溃报告吗?"; @@ -340,6 +344,9 @@ "screen_advanced_settings_element_call_base_url" = "自定义 Element Call URL"; "screen_advanced_settings_element_call_base_url_description" = "为 Element 通话设置根 URL。"; "screen_advanced_settings_element_call_base_url_validation_error" = "URL 无效,请确保包含协议(http/https)和正确的地址。"; +"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"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "正在加载消息..."; "screen_room_pinned_banner_view_all_button_title" = "查看全部"; "screen_room_details_pinned_events_row_title" = "置顶消息"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "消息未发送,因为%1$@尚未验证所有设备。"; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "您即将在 %@ 上创建一个帐户"; "screen_advanced_settings_developer_mode" = "开发者模式"; "screen_advanced_settings_developer_mode_description" = "允许开发人员访问特性和功能。"; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "禁用富文本编辑器,手动输入 Markdown。"; "screen_advanced_settings_send_read_receipts" = "已读回执"; "screen_advanced_settings_send_read_receipts_description" = "如果关闭,您的已读回执将不会发送给别人。您仍能收到别人的已读回执。"; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "开启备份"; "screen_chat_backup_key_backup_description" = "备份可确保你不会丢失消息历史记录。%1$@。"; "screen_chat_backup_key_backup_title" = "备份"; +"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" = "更改恢复密钥"; +"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" = "您的聊天备份当前不同步。"; "screen_chat_backup_recovery_action_setup" = "设置恢复密钥"; "screen_chat_backup_recovery_action_setup_description" = "在丢失或从 %1$@ 登出所有设备的情况下访问加密消息。"; @@ -621,7 +634,6 @@ "screen_qr_code_login_verify_code_title" = "您的验证码"; "screen_recovery_key_change_description" = "如果您丢失了现有的恢复密钥,请获取新的恢复密钥。更改恢复密钥后,您的旧密钥将不再起作用。"; "screen_recovery_key_change_generate_key" = "生成新的恢复密钥"; -"screen_recovery_key_change_generate_key_description" = "确保您可以将恢复密钥存储在安全的地方"; "screen_recovery_key_change_success" = "恢复密钥已更改"; "screen_recovery_key_change_title" = "更改恢复密钥?"; "screen_recovery_key_confirm_create_new_recovery_key" = "创建新的恢复密钥"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "比较一组表情符号。"; "screen_session_verification_request_accepted_subtitle" = "比较表情符号,确保它们以相同顺序排列。"; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "不匹配"; "screen_session_verification_they_match" = "匹配"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix 是一个用于安全、去中心化通信的开放网络。"; "screen_notification_settings_mentions_section_title" = "提及"; "screen_qr_code_login_invalid_scan_state_retry_button" = "再试一次"; +"screen_recovery_key_change_generate_key_description" = "确保将恢复密钥存储在安全的地方"; "screen_recovery_key_confirm_title" = "输入恢复密钥"; "screen_report_content_block_user" = "封禁用户"; "screen_reset_encryption_password_placeholder" = "输入……"; diff --git a/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings b/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings index 48581fa75b..458c278852 100644 --- a/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings @@ -139,6 +139,7 @@ "common_edited_suffix" = "(已編輯)"; "common_editing" = "編輯中"; "common_emote" = "* %1$@ %2$@"; +"common_encryption" = "Encryption"; "common_encryption_enabled" = "已啟用加密"; "common_enter_your_pin" = "輸入您的 PIN 碼"; "common_error" = "錯誤"; @@ -149,6 +150,7 @@ "common_favourited" = "我的最愛"; "common_file" = "檔案"; "common_forward_message" = "轉寄訊息"; +"common_frequently_used" = "Frequently used"; "common_gif" = "GIF"; "common_image" = "圖片"; "common_in_reply_to" = "回覆 %1$@"; @@ -241,6 +243,8 @@ "common.pinned" = "Pinned"; "common.send_to" = "傳送給"; "common.you" = "You"; +"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; +"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup."; "confirm_recovery_key_banner_title" = "輸入您的復原金鑰"; "crash_detection_dialog_content" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; @@ -340,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"; @@ -369,6 +376,7 @@ "screen_room_pinned_banner_loading_description" = "Loading message…"; "screen_room_pinned_banner_view_all_button_title" = "View All"; "screen_room_details_pinned_events_row_title" = "Pinned messages"; +"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; @@ -380,6 +388,8 @@ "screen_account_provider_signup_title" = "您即將在 %@ 建立帳號"; "screen_advanced_settings_developer_mode" = "開發者模式"; "screen_advanced_settings_developer_mode_description" = "Enable to have access to features and functionality for developers."; +"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "手動輸入 Markdown,停用格式化文字編輯器。"; "screen_advanced_settings_send_read_receipts" = "已讀回條"; "screen_advanced_settings_send_read_receipts_description" = "If turned off, your read receipts won't be sent to anyone. You will still receive read receipts from other users."; @@ -448,7 +458,10 @@ "screen_chat_backup_key_backup_action_enable" = "開啟備份功能"; "screen_chat_backup_key_backup_description" = "備份可確保您不會遺失歷史訊息。%1$@。"; "screen_chat_backup_key_backup_title" = "備份"; +"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" = "變更復原金鑰"; +"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."; @@ -621,7 +634,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"; @@ -635,14 +647,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" = "點擊以複製復原金鑰"; -"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"; @@ -824,8 +836,12 @@ "screen_session_verification_ready_subtitle" = "比對一組唯一的表情符號。"; "screen_session_verification_request_accepted_subtitle" = "表情符號是唯一的,請相互比對,確認它們的排列順序是否相同。"; "screen_session_verification_request_details_timestamp" = "Signed in"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; +"screen_session_verification_request_success_title" = "Device verified"; "screen_session_verification_request_title" = "Verification requested"; "screen_session_verification_they_dont_match" = "不一樣"; "screen_session_verification_they_match" = "一樣"; @@ -1012,6 +1028,7 @@ "screen_login_subtitle" = "Matrix 是一個開放網路,為了安全且去中心化的通訊而生。"; "screen_notification_settings_mentions_section_title" = "提及"; "screen_qr_code_login_invalid_scan_state_retry_button" = "再試一次"; +"screen_recovery_key_change_generate_key_description" = "Do not share this with anyone!"; "screen_recovery_key_confirm_title" = "輸入您的復原金鑰"; "screen_report_content_block_user" = "封鎖使用者"; "screen_reset_encryption_password_placeholder" = "Enter…"; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 04e3312aad..7e52a63289 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -834,6 +834,10 @@ internal enum L10n { internal static var screenAdvancedSettingsElementCallBaseUrlDescription: String { return L10n.tr("Localizable", "screen_advanced_settings_element_call_base_url_description") } /// Invalid URL, please make sure you include the protocol (http/https) and the correct address. internal static var screenAdvancedSettingsElementCallBaseUrlValidationError: String { return L10n.tr("Localizable", "screen_advanced_settings_element_call_base_url_validation_error") } + /// Optimize for upload + internal static var screenAdvancedSettingsMediaCompressionDescription: String { return L10n.tr("Localizable", "screen_advanced_settings_media_compression_description") } + /// Media + internal static var screenAdvancedSettingsMediaCompressionTitle: String { return L10n.tr("Localizable", "screen_advanced_settings_media_compression_title") } /// Disable the rich text editor to type Markdown manually. internal static var screenAdvancedSettingsRichTextEditorDescription: String { return L10n.tr("Localizable", "screen_advanced_settings_rich_text_editor_description") } /// Read receipts diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Public-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Public-Room.png index 494741c06c..dd7544c1ee 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Public-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Public-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1c6b345563649b24c3bda6b427c27f10965eb2bf7c0e0a1c7c58db3949d03e1 -size 175943 +oid sha256:d3a984e4fc4d1aade45c5e8b98dcba2f1ecc0e50965e2ca64ad6faad777de26c +size 172668 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Room-without-users.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Room-without-users.png index bc1f6a8fe9..57d2a78428 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Room-without-users.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Room-without-users.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec0202e335366e132947b3f4d1c97ce82e105acf45f398b9cf2bbe55414588ee -size 147726 +oid sha256:49a518ac1fe0ffd8719d757b5c5f2decde92b237937f68df15a239c228cf24b8 +size 144080 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Room.png index 11c561c9fc..6c198b387e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f4a9dc7137a6a072e2e0d76bdc5e8c22ecdaac32872bee5a6dc0179f19aabf5 -size 147402 +oid sha256:467d13af04d60db15fb1586df63b7589a59ae6f2cb4c7482b79cc0c56d7af0ce +size 143822 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Public-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Public-Room.png index 321e4f6c8f..2bb300bfb9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Public-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Public-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:091968b37da82fb3f2ac667a605dc7336454a5fb96a1d23ba1ffde9c39bec622 -size 211937 +oid sha256:0f493e2b0a85be6d277f7138a11f0f02510b04df3fc22a8c76391fbc3444abd0 +size 208736 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Room-without-users.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Room-without-users.png index 69e54e34a9..d4accf96e4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Room-without-users.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Room-without-users.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3dbbb98babb4ce5d4289bdf174c4e621a68be3ad11b4567f42e95d8884e006f1 -size 171700 +oid sha256:c03a493641d0f7c21680bec4ccea98428f210d642011feb3d61988aa09af2612 +size 168502 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Room.png index 672109113e..927c1e3d61 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d6f0a23bbebc6d474b3e79269d507674016ec47dda0a03c018f660274c8f041 -size 173042 +oid sha256:1b8555d5ddea49cf85fc2fc9cb8981f9453f30152f8befc4d596358a7c79f63d +size 169943 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Public-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Public-Room.png index 1a3b80ed0e..53af40f6dd 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Public-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Public-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d3e33cc4aca21c34c9975cfca6e9002579ab4d1c9e73ce6320c4768a47f7f4d -size 123703 +oid sha256:7f54b4053b937e1fc6cee22dfff905bead3ccfbd45bf6607fb8492c74dda15fd +size 118717 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Room-without-users.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Room-without-users.png index ffe3ec6871..9280f48615 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Room-without-users.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Room-without-users.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b8e2e588467837b31e03191cb116f437038dbb5fde756b2f80eeff10fe25ae8 -size 99369 +oid sha256:133221a0b8d4b9aed307a19043b323ca99c3cf639b2097c3713209c6c69c70e5 +size 94718 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Room.png index 7406d01af1..9699119670 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3883d12090f025447534784ee3067ead0abb6a91f7128b2f9616633e64548665 -size 100102 +oid sha256:5c99bf8cd5ba59b6343a3e61af3f2b45ebbe3cab0dda38ef6aa45750b7c13558 +size 94927 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Public-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Public-Room.png index 16fc435376..cfb427967f 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Public-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Public-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ba52fb45501af7ba6e80339749c38e6a975fbe58163c62c7ffe23e2eddc3e3b -size 164555 +oid sha256:2155e204f2bf31a62cc09e3656e77fa58ee327a1ba37de0a307ba7e4fdd9a836 +size 162088 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Room-without-users.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Room-without-users.png index 5afeabb87d..6848b26ade 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Room-without-users.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Room-without-users.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3bedde91879578d668626ee76161ec184854b4e950fbf51b68fc3733debae0b8 -size 138817 +oid sha256:608dbe5519a764e6a9dd8c1d909b5d53ec969d34a03251cdedaabdc1508d661a +size 124622 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Room.png index ec55d698f7..20411e7c1c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Room.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Room.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db3ba5d41ecb661e54371b24970734e686f5ab7d6bec5150a0b4f8d7b8ada1d3 -size 139505 +oid sha256:8f5dd8e86929e05e9a30b27fe8895668246da009696ac614f8f4fbf4bf78d0d8 +size 125070 From 589df7d76edaebdd69958e49430c2fbe3369841b Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:28:05 +0000 Subject: [PATCH 089/114] Use an https callback for OIDC once again. (#3461) * Use the new WAS callback type and return back to the https callback for OIDC. * Simplify OIDCAuthenticationPresenter now it doesn't need to handle universal links. * Remove old unit tests. --- .../Sources/Application/AppCoordinator.swift | 6 -- .../Sources/Application/AppSettings.swift | 10 +- .../Application/Navigation/AppRoutes.swift | 13 --- .../AuthenticationFlowCoordinator.swift | 9 -- .../RoomFlowCoordinator.swift | 2 +- .../UserSessionFlowCoordinator.swift | 2 - .../OIDCAuthenticationPresenter.swift | 97 ++++++------------- .../SoftLogoutScreenCoordinator.swift | 9 -- .../OIDCAccountSettingsPresenter.swift | 2 +- .../Sources/AppRouteURLParserTests.swift | 27 ------ 10 files changed, 34 insertions(+), 143 deletions(-) diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index 1b878c7851..b46a3aad5d 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -195,12 +195,6 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg if let route = appRouteURLParser.route(from: url) { switch route { - case .oidcCallback(let url): - if stateMachine.state == .softLogout { - softLogoutCoordinator?.handleOIDCRedirectURL(url) - } else { - authenticationFlowCoordinator?.handleOIDCRedirectURL(url) - } case .genericCallLink(let url): if let userSessionFlowCoordinator { userSessionFlowCoordinator.handleAppRoute(route, animated: true) diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index 4762449230..c759358e23 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -153,14 +153,8 @@ final class AppSettings { /// Any pre-defined static client registrations for OIDC issuers. let oidcStaticRegistrations: [URL: String] = ["https://id.thirdroom.io/realms/thirdroom": "elementx"] - /// The redirect URL used for OIDC. - let oidcRedirectURL = { - guard let url = URL(string: "\(InfoPlistReader.main.appScheme):/callback") else { - fatalError("Invalid OIDC redirect URL") - } - - return url - }() + /// The redirect URL used for OIDC. This no longer uses universal links so we don't need the bundle ID to avoid conflicts between Element X, Nightly and PR builds. + let oidcRedirectURL: URL = "https://element.io/oidc/login" private(set) lazy var oidcConfiguration = OIDCConfigurationProxy(clientName: InfoPlistReader.main.bundleDisplayName, redirectURI: oidcRedirectURL, diff --git a/ElementX/Sources/Application/Navigation/AppRoutes.swift b/ElementX/Sources/Application/Navigation/AppRoutes.swift index 507e77b4b9..d90b9282a5 100644 --- a/ElementX/Sources/Application/Navigation/AppRoutes.swift +++ b/ElementX/Sources/Application/Navigation/AppRoutes.swift @@ -9,8 +9,6 @@ import Foundation import MatrixRustSDK enum AppRoute: Equatable { - /// The callback used to complete login with OIDC. - case oidcCallback(url: URL) /// The app's home screen. case roomList /// A room, shown as the root of the stack (popping any child rooms). @@ -52,7 +50,6 @@ struct AppRouteURLParser { urlParsers = [ MatrixPermalinkParser(), ElementWebURLParser(domains: appSettings.elementWebHosts), - OIDCCallbackURLParser(appSettings: appSettings), ElementCallURLParser() ] } @@ -76,16 +73,6 @@ protocol URLParser { func route(from url: URL) -> AppRoute? } -/// The parser for the OIDC callback URL. This always returns a `.oidcCallback`. -struct OIDCCallbackURLParser: URLParser { - let appSettings: AppSettings - - func route(from url: URL) -> AppRoute? { - guard url.absoluteString.starts(with: appSettings.oidcRedirectURL.absoluteString) else { return nil } - return .oidcCallback(url: url) - } -} - /// The parser for Element Call links. This always returns a `.genericCallLink`. struct ElementCallURLParser: URLParser { private let knownHosts = ["call.element.io"] diff --git a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift index 5edc7967dd..7a93de1a0b 100644 --- a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift @@ -65,15 +65,6 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { fatalError() } - func handleOIDCRedirectURL(_ url: URL) { - guard let oidcPresenter else { - MXLog.error("Failed to find an OIDC request in progress.") - return - } - - oidcPresenter.handleUniversalLinkCallback(url) - } - // MARK: - Private private func showStartScreen() { diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index 89faaa1c86..fd84208489 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -163,7 +163,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { } case .roomAlias, .childRoomAlias, .eventOnRoomAlias, .childEventOnRoomAlias: break // These are converted to a room ID route one level above. - case .roomList, .userProfile, .call, .genericCallLink, .oidcCallback, .settings, .chatBackupSettings: + case .roomList, .userProfile, .call, .genericCallLink, .settings, .chatBackupSettings: break // These routes can't be handled. } } diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift index c5b64b2208..37a960b68f 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift @@ -282,8 +282,6 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { presentCallScreen(genericCallLink: url) case .settings, .chatBackupSettings: settingsFlowCoordinator.handleAppRoute(appRoute, animated: animated) - case .oidcCallback: - break } } diff --git a/ElementX/Sources/Screens/Authentication/OIDCAuthenticationPresenter.swift b/ElementX/Sources/Screens/Authentication/OIDCAuthenticationPresenter.swift index 03b20e502e..d4f85f83e1 100644 --- a/ElementX/Sources/Screens/Authentication/OIDCAuthenticationPresenter.swift +++ b/ElementX/Sources/Screens/Authentication/OIDCAuthenticationPresenter.swift @@ -14,16 +14,6 @@ class OIDCAuthenticationPresenter: NSObject { private let oidcRedirectURL: URL private let presentationAnchor: UIWindow - /// The data required to complete a request. - struct Request { - let session: ASWebAuthenticationSession - let oidcData: OIDCAuthorizationDataProxy - let continuation: CheckedContinuation, Never> - } - - /// The current request in progress. This is a single use value and will be moved on access. - @Consumable private var request: Request? - init(authenticationService: AuthenticationServiceProtocol, oidcRedirectURL: URL, presentationAnchor: UIWindow) { self.authenticationService = authenticationService self.oidcRedirectURL = oidcRedirectURL @@ -33,74 +23,35 @@ class OIDCAuthenticationPresenter: NSObject { /// Presents a web authentication session for the supplied data. func authenticate(using oidcData: OIDCAuthorizationDataProxy) async -> Result { - await withCheckedContinuation { continuation in - let session = ASWebAuthenticationSession(url: oidcData.url, - callbackURLScheme: oidcRedirectURL.scheme) { [weak self] url, error in - // This closure won't be called if the scheme is https, see handleUniversalLinkCallback for more info. - guard let self else { return } - - guard let url else { - // Check for user cancellation to avoid showing an alert in that instance. - if let nsError = error as? NSError, - nsError.domain == ASWebAuthenticationSessionErrorDomain, - nsError.code == ASWebAuthenticationSessionError.canceledLogin.rawValue { - self.completeAuthentication(throwing: .oidcError(.userCancellation)) - return - } - - self.completeAuthentication(throwing: .oidcError(.unknown)) - return - } - - completeAuthentication(callbackURL: url) + let (url, error) = await withCheckedContinuation { continuation in + let session = ASWebAuthenticationSession(url: oidcData.url, callback: .oidcRedirectURL(oidcRedirectURL)) { url, error in + continuation.resume(returning: (url, error)) } session.prefersEphemeralWebBrowserSession = false session.presentationContextProvider = self - request = Request(session: session, oidcData: oidcData, continuation: continuation) - session.start() } - } - - /// This method will be used if the `appSettings.oidcRedirectURL`'s scheme is `https`. - /// When using a custom scheme, the redirect will be handled by the web auth session's closure. - func handleUniversalLinkCallback(_ url: URL) { - completeAuthentication(callbackURL: url) - } - - /// Completes the authentication by exchanging the callback URL for a user session. - private func completeAuthentication(callbackURL: URL) { - guard let request else { - MXLog.error("Failed to complete authentication. Missing request.") - return - } - if callbackURL.scheme?.starts(with: "http") == true { - request.session.cancel() - } - - Task { - switch await authenticationService.loginWithOIDCCallback(callbackURL, data: request.oidcData) { - case .success(let userSession): - request.continuation.resume(returning: .success(userSession)) - case .failure(let error): - request.continuation.resume(returning: .failure(error)) + guard let url else { + // Check for user cancellation to avoid showing an alert in that instance. + if let nsError = error as? NSError, + nsError.domain == ASWebAuthenticationSessionErrorDomain, + nsError.code == ASWebAuthenticationSessionError.canceledLogin.rawValue { + await authenticationService.abortOIDCLogin(data: oidcData) + return .failure(.oidcError(.userCancellation)) } - } - } - - /// Aborts the authentication with the supplied error. - private func completeAuthentication(throwing error: AuthenticationServiceError) { - guard let request else { - MXLog.error("Failed to throw authentication error. Missing request.") - return + + await authenticationService.abortOIDCLogin(data: oidcData) + return .failure(.oidcError(.unknown)) } - Task { - await authenticationService.abortOIDCLogin(data: request.oidcData) - request.continuation.resume(returning: .failure(error)) + switch await authenticationService.loginWithOIDCCallback(url, data: oidcData) { + case .success(let userSession): + return .success(userSession) + case .failure(let error): + return .failure(error) } } } @@ -110,3 +61,15 @@ class OIDCAuthenticationPresenter: NSObject { extension OIDCAuthenticationPresenter: ASWebAuthenticationPresentationContextProviding { func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { presentationAnchor } } + +extension ASWebAuthenticationSession.Callback { + static func oidcRedirectURL(_ url: URL) -> Self { + if url.scheme == "https", let host = url.host() { + .https(host: host, path: url.path()) + } else if let scheme = url.scheme { + .customScheme(scheme) + } else { + fatalError("Invalid OIDC redirect URL: \(url)") + } + } +} diff --git a/ElementX/Sources/Screens/Authentication/SoftLogoutScreen/SoftLogoutScreenCoordinator.swift b/ElementX/Sources/Screens/Authentication/SoftLogoutScreen/SoftLogoutScreenCoordinator.swift index db748aa4f6..e306d2c352 100644 --- a/ElementX/Sources/Screens/Authentication/SoftLogoutScreen/SoftLogoutScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Authentication/SoftLogoutScreen/SoftLogoutScreenCoordinator.swift @@ -85,15 +85,6 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol { AnyView(SoftLogoutScreen(context: viewModel.context)) } - func handleOIDCRedirectURL(_ url: URL) { - guard let oidcPresenter else { - MXLog.error("Failed to find an OIDC request in progress.") - return - } - - oidcPresenter.handleUniversalLinkCallback(url) - } - // MARK: - Private private static let loadingIndicatorIdentifier = "\(SoftLogoutScreenCoordinator.self)-Loading" diff --git a/ElementX/Sources/Screens/Settings/AccountSettings/OIDCAccountSettingsPresenter.swift b/ElementX/Sources/Screens/Settings/AccountSettings/OIDCAccountSettingsPresenter.swift index c6980de8eb..3be0d1a2a2 100644 --- a/ElementX/Sources/Screens/Settings/AccountSettings/OIDCAccountSettingsPresenter.swift +++ b/ElementX/Sources/Screens/Settings/AccountSettings/OIDCAccountSettingsPresenter.swift @@ -28,7 +28,7 @@ class OIDCAccountSettingsPresenter: NSObject { /// Presents a web authentication session for the supplied data. func start() { - let session = ASWebAuthenticationSession(url: accountURL, callbackURLScheme: oidcRedirectURL.scheme) { _, _ in } + let session = ASWebAuthenticationSession(url: accountURL, callback: .oidcRedirectURL(oidcRedirectURL)) { _, _ in } session.prefersEphemeralWebBrowserSession = false session.presentationContextProvider = self session.start() diff --git a/UnitTests/Sources/AppRouteURLParserTests.swift b/UnitTests/Sources/AppRouteURLParserTests.swift index 952e249f80..c24c4c8c21 100644 --- a/UnitTests/Sources/AppRouteURLParserTests.swift +++ b/UnitTests/Sources/AppRouteURLParserTests.swift @@ -73,33 +73,6 @@ class AppRouteURLParserTests: XCTestCase { XCTAssertEqual(appRouteURLParser.route(from: customSchemeURL), nil) } - func testOIDCCallbackRoute() { - // Given an OIDC callback for this app. - let callbackURL = appSettings.oidcRedirectURL.appending(queryItems: [URLQueryItem(name: "state", value: "12345"), - URLQueryItem(name: "code", value: "67890")]) - - // When parsing that route. - let route = appRouteURLParser.route(from: callbackURL) - - // Then it should be considered a valid OIDC callback. - XCTAssertEqual(route, AppRoute.oidcCallback(url: callbackURL)) - } - - func testOIDCCallbackAppVariantRoute() { - // Given an OIDC callback for a different app variant. - let callbackURL = appSettings.oidcRedirectURL - .deletingLastPathComponent() - .appending(component: "elementz") - .appending(queryItems: [URLQueryItem(name: "state", value: "12345"), - URLQueryItem(name: "code", value: "67890")]) - - // When parsing that route in this app. - let route = appRouteURLParser.route(from: callbackURL) - - // Then the route shouldn't be considered valid and should be ignored. - XCTAssertEqual(route, nil) - } - func testMatrixUserURL() { let userID = "@test:matrix.org" guard let url = URL(string: "https://matrix.to/#/\(userID)") else { From cf5e9fb31381283495b0983c6da61d412fad90f0 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:44:46 +0000 Subject: [PATCH 090/114] Switch to optimised video uploads to 720p (#3462) --- .../Media/MediaUploadingPreprocessor.swift | 2 +- .../Sources/MediaUploadingPreprocessorTests.swift | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift b/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift index 9a9b0bcefc..2c77e549fe 100644 --- a/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift +++ b/ElementX/Sources/Services/Media/MediaUploadingPreprocessor.swift @@ -389,7 +389,7 @@ struct MediaUploadingPreprocessor { /// - Returns: the URL for the resulting video and its media info as a `VideoProcessingResult` private func convertVideoToMP4(_ url: URL, targetFileSize: UInt = 0) async throws(MediaUploadingPreprocessorError) -> VideoProcessingInfo { let asset = AVURLAsset(url: url) - let presetName = appSettings.optimizeMediaUploads ? AVAssetExportPreset640x480 : AVAssetExportPreset1920x1080 + let presetName = appSettings.optimizeMediaUploads ? AVAssetExportPreset1280x720 : AVAssetExportPreset1920x1080 guard let exportSession = AVAssetExportSession(asset: asset, presetName: presetName) else { throw .failedConvertingVideo diff --git a/UnitTests/Sources/MediaUploadingPreprocessorTests.swift b/UnitTests/Sources/MediaUploadingPreprocessorTests.swift index a9aa74bd59..f8742fca4c 100644 --- a/UnitTests/Sources/MediaUploadingPreprocessorTests.swift +++ b/UnitTests/Sources/MediaUploadingPreprocessorTests.swift @@ -101,10 +101,10 @@ final class MediaUploadingPreprocessorTests: XCTestCase { // Check optimised video info XCTAssertEqual(optimizedVideoInfo.mimetype, "video/mp4") - XCTAssertEqual(optimizedVideoInfo.blurhash, "K32PJbx^I7jYaebHMvV?o$") - XCTAssertEqual(optimizedVideoInfo.size ?? 0, 4_090_898, accuracy: 100) // Note: This is slightly stupid because it is larger now 🤦‍♂️ - XCTAssertEqual(optimizedVideoInfo.width, 640) - XCTAssertEqual(optimizedVideoInfo.height, 360) + XCTAssertEqual(optimizedVideoInfo.blurhash, "K22PJZx^DgadWAbbMuRio$") + XCTAssertEqual(optimizedVideoInfo.size ?? 0, 1_431_959, accuracy: 100) // Note: This is slightly stupid because it is larger now 🤦‍♂️ + XCTAssertEqual(optimizedVideoInfo.width, 1280) + XCTAssertEqual(optimizedVideoInfo.height, 720) XCTAssertEqual(optimizedVideoInfo.duration ?? 0, 30, accuracy: 100) } @@ -165,9 +165,9 @@ final class MediaUploadingPreprocessorTests: XCTestCase { // Check optimised video info XCTAssertEqual(optimizedVideoInfo.mimetype, "video/mp4") XCTAssertEqual(optimizedVideoInfo.blurhash, "K7BDNJD*0L%#sl_2~C9ZE1") - XCTAssertEqual(optimizedVideoInfo.size ?? 0, 6_520_897, accuracy: 100) - XCTAssertEqual(optimizedVideoInfo.width, 360) - XCTAssertEqual(optimizedVideoInfo.height, 640) + XCTAssertEqual(optimizedVideoInfo.size ?? 0, 21_936_767, accuracy: 100) + XCTAssertEqual(optimizedVideoInfo.width, 720) + XCTAssertEqual(optimizedVideoInfo.height, 1280) XCTAssertEqual(optimizedVideoInfo.duration ?? 0, 30, accuracy: 100) } From d77bb935b75368f9fe5942444aef2a892e650fa6 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Tue, 29 Oct 2024 15:21:17 +0200 Subject: [PATCH 091/114] Incoming session verification support (#3428) * Fixes #1227 - Add support for receiving and interacting with incoming session verification requests. * Fix a couple of random small warnings * Move static view config to the view state * Update snapshots --- ElementX.xcodeproj/project.pbxproj | 6 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- .../en.lproj/Localizable.strings | 17 +- .../OnboardingFlowCoordinator.swift | 11 +- .../UserSessionFlowCoordinator.swift | 211 +++++++++++------- ElementX/Sources/Generated/Strings.swift | 18 +- ElementX/Sources/Mocks/ClientProxyMock.swift | 1 - .../Mocks/Generated/GeneratedMocks.swift | 207 +++++++++++------ .../Mocks/Generated/SDKGeneratedMocks.swift | 155 +++++++------ ...ssionVerificationControllerProxyMock.swift | 18 +- .../Other/AccessibilityIdentifiers.swift | 2 + ...SessionVerificationScreenCoordinator.swift | 10 +- .../SessionVerificationScreenModels.swift | 77 +++++-- ...essionVerificationScreenStateMachine.swift | 19 +- .../SessionVerificationScreenViewModel.swift | 51 ++++- ...essionVerificationRequestDetailsView.swift | 80 +++++++ .../View/SessionVerificationScreen.swift | 139 ++++++------ .../Timeline/TimelineInteractionHandler.swift | 1 + .../Sources/Services/Client/ClientProxy.swift | 63 ++++-- .../Services/Client/ClientProxyProtocol.swift | 4 +- .../ElementCall/ElementCallService.swift | 7 +- .../SessionVerificationControllerProxy.swift | 76 ++++++- ...nVerificationControllerProxyProtocol.swift | 20 +- .../UITests/UITestsAppCoordinator.swift | 3 +- .../Sources/GeneratedPreviewTests.swift | 6 + ...ackupKeyBackupScreen-iPad-en-GB.Set-up.png | 4 +- ...ckupKeyBackupScreen-iPad-pseudo.Set-up.png | 4 +- ...KeyBackupScreen-iPhone-16-en-GB.Set-up.png | 4 +- ...eyBackupScreen-iPhone-16-pseudo.Set-up.png | 4 +- ...ecoveryKeyScreen-iPad-en-GB.Generating.png | 4 +- ...ecoveryKeyScreen-iPad-en-GB.Not-set-up.png | 4 +- ...coveryKeyScreen-iPad-pseudo.Generating.png | 4 +- ...coveryKeyScreen-iPad-pseudo.Not-set-up.png | 4 +- ...ryKeyScreen-iPhone-16-en-GB.Generating.png | 4 +- ...ryKeyScreen-iPhone-16-en-GB.Not-set-up.png | 4 +- ...yKeyScreen-iPhone-16-pseudo.Generating.png | 4 +- ...yKeyScreen-iPhone-16-pseudo.Not-set-up.png | 4 +- ...ication-iPad-en-GB.Accepting-Challenge.png | 3 + ...d-en-GB.Accepting-Verification-Request.png | 3 + ...ssionVerification-iPad-en-GB.Cancelled.png | 4 +- ...ication-iPad-en-GB.Declining-Challenge.png | 3 + ...fication-iPad-en-GB.Initial-Initiator.png} | 0 ...ification-iPad-en-GB.Initial-Responder.png | 3 + ...ion-iPad-en-GB.Requesting-Verification.png | 4 +- ...on-iPad-en-GB.SAS-Verification-started.png | 3 + ...ification-iPad-en-GB.Showing-Challenge.png | 4 +- ...n-iPad-en-GB.Starting-SAS-Verification.png | 3 + ...cation-iPad-pseudo.Accepting-Challenge.png | 3 + ...-pseudo.Accepting-Verification-Request.png | 3 + ...sionVerification-iPad-pseudo.Cancelled.png | 4 +- ...cation-iPad-pseudo.Declining-Challenge.png | 3 + ...ication-iPad-pseudo.Initial-Initiator.png} | 0 ...fication-iPad-pseudo.Initial-Responder.png | 3 + ...on-iPad-pseudo.Requesting-Verification.png | 4 +- ...n-iPad-pseudo.SAS-Verification-started.png | 3 + ...fication-iPad-pseudo.Showing-Challenge.png | 4 +- ...-iPad-pseudo.Starting-SAS-Verification.png | 3 + ...on-iPhone-16-en-GB.Accepting-Challenge.png | 3 + ...6-en-GB.Accepting-Verification-Request.png | 3 + ...Verification-iPhone-16-en-GB.Cancelled.png | 4 +- ...on-iPhone-16-en-GB.Declining-Challenge.png | 3 + ...ion-iPhone-16-en-GB.Initial-Initiator.png} | 0 ...tion-iPhone-16-en-GB.Initial-Responder.png | 3 + ...Phone-16-en-GB.Requesting-Verification.png | 4 +- ...hone-16-en-GB.SAS-Verification-started.png | 3 + ...tion-iPhone-16-en-GB.Showing-Challenge.png | 4 +- ...one-16-en-GB.Starting-SAS-Verification.png | 3 + ...n-iPhone-16-pseudo.Accepting-Challenge.png | 3 + ...-pseudo.Accepting-Verification-Request.png | 3 + ...erification-iPhone-16-pseudo.Cancelled.png | 4 +- ...n-iPhone-16-pseudo.Declining-Challenge.png | 3 + ...on-iPhone-16-pseudo.Initial-Initiator.png} | 0 ...ion-iPhone-16-pseudo.Initial-Responder.png | 3 + ...hone-16-pseudo.Requesting-Verification.png | 4 +- ...one-16-pseudo.SAS-Verification-started.png | 3 + ...ion-iPhone-16-pseudo.Showing-Challenge.png | 4 +- ...ne-16-pseudo.Starting-SAS-Verification.png | 3 + ...icationRequestDetailsView-iPad-en-GB.1.png | 3 + ...cationRequestDetailsView-iPad-pseudo.1.png | 3 + ...onRequestDetailsView-iPhone-16-en-GB.1.png | 3 + ...nRequestDetailsView-iPhone-16-pseudo.1.png | 3 + ...teRoom-1-iPad-10th-generation-en-GB.UI.png | 4 +- .../createRoom-1-iPhone-16-en-GB.UI.png | 4 +- ...eateRoom-iPad-10th-generation-en-GB.UI.png | 4 +- .../createRoom-iPhone-16-en-GB.UI.png | 4 +- ...mNoUsers-iPad-10th-generation-en-GB.UI.png | 4 +- .../createRoomNoUsers-iPhone-16-en-GB.UI.png | 4 +- ...cation-0-iPad-10th-generation-en-GB.UI.png | 4 +- ...cation-1-iPad-10th-generation-en-GB.UI.png | 4 +- ...ssionVerification-1-iPhone-16-en-GB.UI.png | 4 +- ...cation-2-iPad-10th-generation-en-GB.UI.png | 4 +- ...ssionVerification-2-iPhone-16-en-GB.UI.png | 4 +- ...cation-3-iPad-10th-generation-en-GB.UI.png | 4 +- ...ssionVerification-3-iPhone-16-en-GB.UI.png | 4 +- ...cation-4-iPad-10th-generation-en-GB.UI.png | 4 +- ...ssionVerification-4-iPhone-16-en-GB.UI.png | 4 +- ...cation-5-iPad-10th-generation-en-GB.UI.png | 4 +- ...ssionVerification-5-iPhone-16-en-GB.UI.png | 4 +- ...cation-6-iPad-10th-generation-en-GB.UI.png | 4 +- ...cation-7-iPad-10th-generation-en-GB.UI.png | 4 +- ...ssionVerification-7-iPhone-16-en-GB.UI.png | 4 +- ...SessionVerificationStateMachineTests.swift | 2 +- .../SessionVerificationViewModelTests.swift | 8 +- project.yml | 2 +- 104 files changed, 995 insertions(+), 483 deletions(-) create mode 100644 ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/View/SessionVerificationRequestDetailsView.swift create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Accepting-Challenge.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Accepting-Verification-Request.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Declining-Challenge.png rename PreviewTests/Sources/__Snapshots__/PreviewTests/{test_sessionVerification-iPad-en-GB.Initial.png => test_sessionVerification-iPad-en-GB.Initial-Initiator.png} (100%) create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Initial-Responder.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.SAS-Verification-started.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Starting-SAS-Verification.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Accepting-Challenge.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Accepting-Verification-Request.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Declining-Challenge.png rename PreviewTests/Sources/__Snapshots__/PreviewTests/{test_sessionVerification-iPad-pseudo.Initial.png => test_sessionVerification-iPad-pseudo.Initial-Initiator.png} (100%) create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Initial-Responder.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.SAS-Verification-started.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Starting-SAS-Verification.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Accepting-Challenge.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Accepting-Verification-Request.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Declining-Challenge.png rename PreviewTests/Sources/__Snapshots__/PreviewTests/{test_sessionVerification-iPhone-16-en-GB.Initial.png => test_sessionVerification-iPhone-16-en-GB.Initial-Initiator.png} (100%) create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Initial-Responder.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.SAS-Verification-started.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Starting-SAS-Verification.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Accepting-Challenge.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Accepting-Verification-Request.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Declining-Challenge.png rename PreviewTests/Sources/__Snapshots__/PreviewTests/{test_sessionVerification-iPhone-16-pseudo.Initial.png => test_sessionVerification-iPhone-16-pseudo.Initial-Initiator.png} (100%) create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Initial-Responder.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.SAS-Verification-started.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Starting-SAS-Verification.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerificationRequestDetailsView-iPad-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerificationRequestDetailsView-iPad-pseudo.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerificationRequestDetailsView-iPhone-16-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerificationRequestDetailsView-iPhone-16-pseudo.1.png diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 62b5b0931c..418ce72929 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -666,6 +666,7 @@ 915B4CDAF220D9AEB4047D45 /* PollInteractionHandlerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 259E5B05BDE6E20C26CF11B4 /* PollInteractionHandlerProtocol.swift */; }; 91ABC91758A6E4A5FAA2E9C4 /* ReadReceipt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 314F1C79850BE46E8ABEAFCB /* ReadReceipt.swift */; }; 91C6AC0E9D2B9C0C76CC6AD4 /* RoomDirectorySearchScreenScreenModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3984C93B8E9B10C92DADF9EE /* RoomDirectorySearchScreenScreenModelProtocol.swift */; }; + 91D1A46A733EC24C081DD353 /* SessionVerificationRequestDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1265FAF2C0AF1C30605BE7 /* SessionVerificationRequestDetailsView.swift */; }; 92012C96039BC8C2CAEBA9E2 /* AuthenticationServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 671C338B7259DC5774816885 /* AuthenticationServiceTests.swift */; }; 9219640F4D980CFC5FE855AD /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 536E72DCBEEC4A1FE66CFDCE /* target.yml */; }; 92720AB0DA9AB5EEF1DAF56B /* SecureBackupLogoutConfirmationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DC017C3CB6B0F7C63F460F2 /* SecureBackupLogoutConfirmationScreenViewModel.swift */; }; @@ -1314,6 +1315,7 @@ 190EC7285D3CFEF0D3011BCF /* GeoURI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeoURI.swift; sourceTree = ""; }; 196004E7695FBA292A7944AF /* ScreenTrackerViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenTrackerViewModifier.swift; sourceTree = ""; }; 1A02406480C351B8C6E0682C /* MediaLoaderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaLoaderProtocol.swift; sourceTree = ""; }; + 1A1265FAF2C0AF1C30605BE7 /* SessionVerificationRequestDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationRequestDetailsView.swift; sourceTree = ""; }; 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Strings+Untranslated.swift"; sourceTree = ""; }; 1A4D29F2683F5772AC72406F /* MapTilerStaticMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStaticMap.swift; sourceTree = ""; }; 1A7ED2EF5BDBAD2A7DBC4636 /* GeoURITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeoURITests.swift; sourceTree = ""; }; @@ -4650,6 +4652,7 @@ A722D372674EE5687E1A67E4 /* View */ = { isa = PBXGroup; children = ( + 1A1265FAF2C0AF1C30605BE7 /* SessionVerificationRequestDetailsView.swift */, 5FACD034DB52525A3CEF2BDF /* SessionVerificationScreen.swift */, ); path = View; @@ -6904,6 +6907,7 @@ 237FC70AA257B935F53316BA /* SessionVerificationControllerProxy.swift in Sources */, AE1A73B24D63DA3D63DC4EE3 /* SessionVerificationControllerProxyMock.swift in Sources */, 94A65DD8A353DF112EBEF67A /* SessionVerificationControllerProxyProtocol.swift in Sources */, + 91D1A46A733EC24C081DD353 /* SessionVerificationRequestDetailsView.swift in Sources */, 707E49BE07E8EB8A13C0EB1E /* SessionVerificationScreen.swift in Sources */, D02DEB36D32A72A1B365E452 /* SessionVerificationScreenCoordinator.swift in Sources */, 5710AAB27D5D866292C1FE06 /* SessionVerificationScreenModels.swift in Sources */, @@ -7828,7 +7832,7 @@ repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.0.61; + version = 1.0.62; }; }; 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 5e7bb11b63..97e4b2e1c1 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/matrix-rust-components-swift", "state" : { - "revision" : "2e6378514e79a648d436e8faeb8cd8106910cf0b", - "version" : "1.0.61" + "revision" : "9b26e40ae6c27c56e233577c863569ff074f84fd", + "version" : "1.0.62" } }, { diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index b16f7076ba..9b14f00f5e 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -115,8 +115,9 @@ "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; "banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; -"banner.set_up_recovery.title" = "Set up recovery"; +"banner_set_up_recovery_submit" = "Set up recovery"; +"banner.set_up_recovery.content" = "Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices."; +"banner.set_up_recovery.title" = "Set up recovery to protect your account"; "common_about" = "About"; "common_acceptable_use_policy" = "Acceptable use policy"; "common_advanced_settings" = "Advanced settings"; @@ -454,7 +455,7 @@ "screen_change_server_form_notice" = "You can only connect to an existing server that supports sliding sync. Your homeserver admin will need to configure it. %1$@"; "screen_change_server_subtitle" = "What is the address of your server?"; "screen_change_server_title" = "Select your server"; -"screen_chat_backup_key_backup_action_disable" = "Turn off backup"; +"screen_chat_backup_key_backup_action_disable" = "Delete key storage"; "screen_chat_backup_key_backup_action_enable" = "Turn on 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"; @@ -538,10 +539,10 @@ "screen_key_backup_disable_confirmation_action_turn_off" = "Turn off"; "screen_key_backup_disable_confirmation_description" = "You will lose your encrypted messages if you are signed out of all devices."; "screen_key_backup_disable_confirmation_title" = "Are you sure you want to turn off backup?"; -"screen_key_backup_disable_description" = "Turning off backup will remove your current encryption key backup and turn off other security features. In this case, you will:"; -"screen_key_backup_disable_description_point_1" = "Not have encrypted message history on new devices"; -"screen_key_backup_disable_description_point_2" = "Lose access to your encrypted messages if you are signed out of %1$@ everywhere"; -"screen_key_backup_disable_title" = "Are you sure you want to turn off backup?"; +"screen_key_backup_disable_description" = "Deleting key storage will remove your cryptographic identity and message keys from the server and turn off the following security features:"; +"screen_key_backup_disable_description_point_1" = "You will not have encrypted message history on new devices"; +"screen_key_backup_disable_description_point_2" = "You will lose access to your encrypted messages if you are signed out of %1$@ everywhere"; +"screen_key_backup_disable_title" = "Are you sure you want to turn off key storage and delete it?"; "screen_login_error_deactivated_account" = "This account has been deactivated."; "screen_login_error_invalid_credentials" = "Incorrect username and/or password"; "screen_login_error_invalid_user_id" = "This is not a valid user identifier. Expected format: ‘@user:homeserver.org’"; @@ -652,7 +653,7 @@ "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_description" = "Your key storage is protected by a recovery key. If you need a new recovery key after setup, you can recreate it by selecting ‘Change recovery key’."; "screen_recovery_key_setup_generate_key" = "Generate your recovery key"; "screen_recovery_key_setup_generate_key_description" = "Do not share this with anyone!"; "screen_recovery_key_setup_success" = "Recovery setup successful"; diff --git a/ElementX/Sources/FlowCoordinators/OnboardingFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/OnboardingFlowCoordinator.swift index e64ff8f465..48428c2a8a 100644 --- a/ElementX/Sources/FlowCoordinators/OnboardingFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/OnboardingFlowCoordinator.swift @@ -244,9 +244,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol { switch action { case .otherDevice: - Task { - await self.presentSessionVerificationScreen() - } + presentSessionVerificationScreen() case .recoveryKey: presentRecoveryKeyScreen() case .skip: @@ -263,12 +261,13 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol { presentCoordinator(coordinator) } - private func presentSessionVerificationScreen() async { - guard case let .success(sessionVerificationController) = await userSession.clientProxy.sessionVerificationControllerProxy() else { + private func presentSessionVerificationScreen() { + guard let sessionVerificationController = userSession.clientProxy.sessionVerificationController else { fatalError("The sessionVerificationController should aways be valid at this point") } - let parameters = SessionVerificationScreenCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationController) + let parameters = SessionVerificationScreenCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationController, + flow: .initiator) let coordinator = SessionVerificationScreenCoordinator(parameters: parameters) diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift index 37a960b68f..012c26dfb9 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift @@ -7,6 +7,7 @@ import AVKit import Combine +import MatrixRustSDK import SwiftUI enum UserSessionFlowCoordinatorAction { @@ -59,7 +60,6 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { /// For testing purposes. var statePublisher: AnyPublisher { stateMachine.statePublisher } - // swiftlint:disable:next function_body_length init(userSession: UserSessionProtocol, navigationRootCoordinator: NavigationRootCoordinator, appLockService: AppLockServiceProtocol, @@ -113,87 +113,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { setupStateMachine() - userSession.sessionSecurityStatePublisher - .map(\.verificationState) - .filter { $0 != .unknown } - .removeDuplicates() - .receive(on: DispatchQueue.main) - .sink { [weak self] _ in - guard let self else { return } - - attemptStartingOnboarding() - } - .store(in: &cancellables) - - settingsFlowCoordinator.actions.sink { [weak self] action in - guard let self else { return } - - switch action { - case .presentedSettings: - stateMachine.processEvent(.showSettingsScreen) - case .dismissedSettings: - stateMachine.processEvent(.dismissedSettingsScreen) - case .runLogoutFlow: - Task { await self.runLogoutFlow() } - case .clearCache: - actionsSubject.send(.clearCache) - case .forceLogout: - actionsSubject.send(.forceLogout) - } - } - .store(in: &cancellables) - - userSession.clientProxy.actionsPublisher - .receive(on: DispatchQueue.main) - .sink { action in - guard case let .receivedDecryptionError(info) = action else { - return - } - - let timeToDecryptMs: Int - if let unsignedTimeToDecryptMs = info.timeToDecryptMs { - timeToDecryptMs = Int(unsignedTimeToDecryptMs) - } else { - timeToDecryptMs = -1 - } - - switch info.cause { - case .unknown: - analytics.trackError(context: nil, domain: .E2EE, name: .UnknownError, timeToDecryptMillis: timeToDecryptMs) - case .unknownDevice: - analytics.trackError(context: nil, domain: .E2EE, name: .ExpectedSentByInsecureDevice, timeToDecryptMillis: timeToDecryptMs) - case .unsignedDevice: - analytics.trackError(context: nil, domain: .E2EE, name: .ExpectedSentByInsecureDevice, timeToDecryptMillis: timeToDecryptMs) - case .verificationViolation: - analytics.trackError(context: nil, domain: .E2EE, name: .ExpectedVerificationViolation, timeToDecryptMillis: timeToDecryptMs) - case .sentBeforeWeJoined: - analytics.trackError(context: nil, domain: .E2EE, name: .ExpectedDueToMembership, timeToDecryptMillis: timeToDecryptMs) - } - } - .store(in: &cancellables) - - elementCallService.actions - .receive(on: DispatchQueue.main) - .sink { [weak self] action in - switch action { - case .endCall: - self?.dismissCallScreenIfNeeded() - default: - break - } - } - .store(in: &cancellables) - - onboardingFlowCoordinator.actions - .sink { [weak self] action in - guard let self else { return } - - switch action { - case .logout: - logout() - } - } - .store(in: &cancellables) + setupObservers() } func start() { @@ -385,6 +305,129 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { } } + private func setupObservers() { + userSession.sessionSecurityStatePublisher + .map(\.verificationState) + .filter { $0 != .unknown } + .removeDuplicates() + .receive(on: DispatchQueue.main) + .sink { [weak self] _ in + guard let self else { return } + + attemptStartingOnboarding() + + setupSessionVerificationRequestsObserver() + } + .store(in: &cancellables) + + settingsFlowCoordinator.actions.sink { [weak self] action in + guard let self else { return } + + switch action { + case .presentedSettings: + stateMachine.processEvent(.showSettingsScreen) + case .dismissedSettings: + stateMachine.processEvent(.dismissedSettingsScreen) + case .runLogoutFlow: + Task { await self.runLogoutFlow() } + case .clearCache: + actionsSubject.send(.clearCache) + case .forceLogout: + actionsSubject.send(.forceLogout) + } + } + .store(in: &cancellables) + + userSession.clientProxy.actionsPublisher + .receive(on: DispatchQueue.main) + .sink { [weak self] action in + guard let self, case let .receivedDecryptionError(info) = action else { + return + } + + let timeToDecryptMs: Int + if let unsignedTimeToDecryptMs = info.timeToDecryptMs { + timeToDecryptMs = Int(unsignedTimeToDecryptMs) + } else { + timeToDecryptMs = -1 + } + + switch info.cause { + case .unknown: + analytics.trackError(context: nil, domain: .E2EE, name: .UnknownError, timeToDecryptMillis: timeToDecryptMs) + case .unknownDevice: + analytics.trackError(context: nil, domain: .E2EE, name: .ExpectedSentByInsecureDevice, timeToDecryptMillis: timeToDecryptMs) + case .unsignedDevice: + analytics.trackError(context: nil, domain: .E2EE, name: .ExpectedSentByInsecureDevice, timeToDecryptMillis: timeToDecryptMs) + case .verificationViolation: + analytics.trackError(context: nil, domain: .E2EE, name: .ExpectedVerificationViolation, timeToDecryptMillis: timeToDecryptMs) + case .sentBeforeWeJoined: + analytics.trackError(context: nil, domain: .E2EE, name: .ExpectedDueToMembership, timeToDecryptMillis: timeToDecryptMs) + } + } + .store(in: &cancellables) + + elementCallService.actions + .receive(on: DispatchQueue.main) + .sink { [weak self] action in + switch action { + case .endCall: + self?.dismissCallScreenIfNeeded() + default: + break + } + } + .store(in: &cancellables) + + onboardingFlowCoordinator.actions + .sink { [weak self] action in + guard let self else { return } + + switch action { + case .logout: + logout() + } + } + .store(in: &cancellables) + } + + private func setupSessionVerificationRequestsObserver() { + userSession.clientProxy.sessionVerificationController?.actions + .receive(on: DispatchQueue.main) + .sink { [weak self] action in + guard let self, case .receivedVerificationRequest(let details) = action else { + return + } + + MXLog.info("Received session verification request") + + presentSessionVerificationScreen(details: details) + } + .store(in: &cancellables) + } + + private func presentSessionVerificationScreen(details: SessionVerificationRequestDetails) { + guard let sessionVerificationController = userSession.clientProxy.sessionVerificationController else { + fatalError("The sessionVerificationController should aways be valid at this point") + } + + let parameters = SessionVerificationScreenCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationController, + flow: .responder(details: details)) + + let coordinator = SessionVerificationScreenCoordinator(parameters: parameters) + + coordinator.actions + .sink { [weak self] action in + switch action { + case .done: + self?.navigationSplitCoordinator.setSheetCoordinator(nil) + } + } + .store(in: &cancellables) + + navigationSplitCoordinator.setSheetCoordinator(coordinator) + } + private func presentHomeScreen() { let parameters = HomeScreenCoordinatorParameters(userSession: userSession, bugReportService: bugReportService, @@ -569,7 +612,9 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { self?.stateMachine.processEvent(.dismissedStartChatScreen) } } - + + // MARK: Session Verification + // MARK: Calls private func presentCallScreen(genericCallLink url: URL) { diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 7e52a63289..087a6974de 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -266,6 +266,8 @@ internal enum L10n { internal static var bannerMigrateToNativeSlidingSyncForceLogoutTitle: String { return L10n.tr("Localizable", "banner_migrate_to_native_sliding_sync_force_logout_title") } /// Upgrade available internal static var bannerMigrateToNativeSlidingSyncTitle: String { return L10n.tr("Localizable", "banner_migrate_to_native_sliding_sync_title") } + /// Set up recovery + internal static var bannerSetUpRecoverySubmit: String { return L10n.tr("Localizable", "banner_set_up_recovery_submit") } /// About internal static var commonAbout: String { return L10n.tr("Localizable", "common_about") } /// Acceptable use policy @@ -1009,7 +1011,7 @@ internal enum L10n { internal static var screenChangeServerSubtitle: String { return L10n.tr("Localizable", "screen_change_server_subtitle") } /// Select your server internal static var screenChangeServerTitle: String { return L10n.tr("Localizable", "screen_change_server_title") } - /// Turn off backup + /// Delete key storage 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") } @@ -1258,15 +1260,15 @@ internal enum L10n { internal static var screenKeyBackupDisableConfirmationDescription: String { return L10n.tr("Localizable", "screen_key_backup_disable_confirmation_description") } /// Are you sure you want to turn off backup? internal static var screenKeyBackupDisableConfirmationTitle: String { return L10n.tr("Localizable", "screen_key_backup_disable_confirmation_title") } - /// Turning off backup will remove your current encryption key backup and turn off other security features. In this case, you will: + /// Deleting key storage will remove your cryptographic identity and message keys from the server and turn off the following security features: internal static var screenKeyBackupDisableDescription: String { return L10n.tr("Localizable", "screen_key_backup_disable_description") } - /// Not have encrypted message history on new devices + /// You will not have encrypted message history on new devices internal static var screenKeyBackupDisableDescriptionPoint1: String { return L10n.tr("Localizable", "screen_key_backup_disable_description_point_1") } - /// Lose access to your encrypted messages if you are signed out of %1$@ everywhere + /// You will lose access to your encrypted messages if you are signed out of %1$@ everywhere internal static func screenKeyBackupDisableDescriptionPoint2(_ p1: Any) -> String { return L10n.tr("Localizable", "screen_key_backup_disable_description_point_2", String(describing: p1)) } - /// Are you sure you want to turn off backup? + /// Are you sure you want to turn off key storage and delete it? internal static var screenKeyBackupDisableTitle: String { return L10n.tr("Localizable", "screen_key_backup_disable_title") } /// This account has been deactivated. internal static var screenLoginErrorDeactivatedAccount: String { return L10n.tr("Localizable", "screen_login_error_deactivated_account") } @@ -1536,7 +1538,7 @@ internal enum L10n { internal static var screenRecoveryKeySetupConfirmationDescription: String { return L10n.tr("Localizable", "screen_recovery_key_setup_confirmation_description") } /// Have you saved your recovery key? internal static var screenRecoveryKeySetupConfirmationTitle: String { return L10n.tr("Localizable", "screen_recovery_key_setup_confirmation_title") } - /// 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’. + /// Your key storage is protected by a recovery key. If you need a new recovery key after setup, you can recreate it by selecting ‘Change recovery key’. 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") } @@ -2499,9 +2501,9 @@ internal enum L10n { internal enum Banner { internal enum SetUpRecovery { - /// Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices. + /// Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices. internal static var content: String { return L10n.tr("Localizable", "banner.set_up_recovery.content") } - /// Set up recovery + /// Set up recovery to protect your account internal static var title: String { return L10n.tr("Localizable", "banner.set_up_recovery.title") } } } diff --git a/ElementX/Sources/Mocks/ClientProxyMock.swift b/ElementX/Sources/Mocks/ClientProxyMock.swift index ac086c1c43..8cc4962f89 100644 --- a/ElementX/Sources/Mocks/ClientProxyMock.swift +++ b/ElementX/Sources/Mocks/ClientProxyMock.swift @@ -60,7 +60,6 @@ extension ClientProxyMock { logoutReturnValue = nil searchUsersSearchTermLimitReturnValue = .success(.init(results: [], limited: false)) profileForReturnValue = .success(.init(userID: "@a:b.com", displayName: "Some user")) - sessionVerificationControllerProxyReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) ignoreUserReturnValue = .success(()) unignoreUserReturnValue = .success(()) diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 2438f6b7f3..c7524379ca 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -2210,6 +2210,7 @@ class ClientProxyMock: ClientProxyProtocol { set(value) { underlyingSecureBackupController = value } } var underlyingSecureBackupController: SecureBackupControllerProtocol! + var sessionVerificationController: SessionVerificationControllerProxyProtocol? //MARK: - isOnlyDeviceLeft @@ -3519,70 +3520,6 @@ class ClientProxyMock: ClientProxyProtocol { return removeUserAvatarReturnValue } } - //MARK: - sessionVerificationControllerProxy - - var sessionVerificationControllerProxyUnderlyingCallsCount = 0 - var sessionVerificationControllerProxyCallsCount: Int { - get { - if Thread.isMainThread { - return sessionVerificationControllerProxyUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = sessionVerificationControllerProxyUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - sessionVerificationControllerProxyUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - sessionVerificationControllerProxyUnderlyingCallsCount = newValue - } - } - } - } - var sessionVerificationControllerProxyCalled: Bool { - return sessionVerificationControllerProxyCallsCount > 0 - } - - var sessionVerificationControllerProxyUnderlyingReturnValue: Result! - var sessionVerificationControllerProxyReturnValue: Result! { - get { - if Thread.isMainThread { - return sessionVerificationControllerProxyUnderlyingReturnValue - } else { - var returnValue: Result? = nil - DispatchQueue.main.sync { - returnValue = sessionVerificationControllerProxyUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - sessionVerificationControllerProxyUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - sessionVerificationControllerProxyUnderlyingReturnValue = newValue - } - } - } - } - var sessionVerificationControllerProxyClosure: (() async -> Result)? - - func sessionVerificationControllerProxy() async -> Result { - sessionVerificationControllerProxyCallsCount += 1 - if let sessionVerificationControllerProxyClosure = sessionVerificationControllerProxyClosure { - return await sessionVerificationControllerProxyClosure() - } else { - return sessionVerificationControllerProxyReturnValue - } - } //MARK: - deactivateAccount var deactivateAccountPasswordEraseDataUnderlyingCallsCount = 0 @@ -13435,12 +13372,146 @@ class SecureBackupControllerMock: SecureBackupControllerProtocol { } } class SessionVerificationControllerProxyMock: SessionVerificationControllerProxyProtocol { - var callbacks: PassthroughSubject { - get { return underlyingCallbacks } - set(value) { underlyingCallbacks = value } + var actions: PassthroughSubject { + get { return underlyingActions } + set(value) { underlyingActions = value } + } + var underlyingActions: PassthroughSubject! + + //MARK: - acknowledgeVerificationRequest + + var acknowledgeVerificationRequestDetailsUnderlyingCallsCount = 0 + var acknowledgeVerificationRequestDetailsCallsCount: Int { + get { + if Thread.isMainThread { + return acknowledgeVerificationRequestDetailsUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = acknowledgeVerificationRequestDetailsUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + acknowledgeVerificationRequestDetailsUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + acknowledgeVerificationRequestDetailsUnderlyingCallsCount = newValue + } + } + } + } + var acknowledgeVerificationRequestDetailsCalled: Bool { + return acknowledgeVerificationRequestDetailsCallsCount > 0 } - var underlyingCallbacks: PassthroughSubject! + var acknowledgeVerificationRequestDetailsReceivedDetails: SessionVerificationRequestDetails? + var acknowledgeVerificationRequestDetailsReceivedInvocations: [SessionVerificationRequestDetails] = [] + var acknowledgeVerificationRequestDetailsUnderlyingReturnValue: Result! + var acknowledgeVerificationRequestDetailsReturnValue: Result! { + get { + if Thread.isMainThread { + return acknowledgeVerificationRequestDetailsUnderlyingReturnValue + } else { + var returnValue: Result? = nil + DispatchQueue.main.sync { + returnValue = acknowledgeVerificationRequestDetailsUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + acknowledgeVerificationRequestDetailsUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + acknowledgeVerificationRequestDetailsUnderlyingReturnValue = newValue + } + } + } + } + var acknowledgeVerificationRequestDetailsClosure: ((SessionVerificationRequestDetails) async -> Result)? + + func acknowledgeVerificationRequest(details: SessionVerificationRequestDetails) async -> Result { + acknowledgeVerificationRequestDetailsCallsCount += 1 + acknowledgeVerificationRequestDetailsReceivedDetails = details + DispatchQueue.main.async { + self.acknowledgeVerificationRequestDetailsReceivedInvocations.append(details) + } + if let acknowledgeVerificationRequestDetailsClosure = acknowledgeVerificationRequestDetailsClosure { + return await acknowledgeVerificationRequestDetailsClosure(details) + } else { + return acknowledgeVerificationRequestDetailsReturnValue + } + } + //MARK: - acceptVerificationRequest + + var acceptVerificationRequestUnderlyingCallsCount = 0 + var acceptVerificationRequestCallsCount: Int { + get { + if Thread.isMainThread { + return acceptVerificationRequestUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = acceptVerificationRequestUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + acceptVerificationRequestUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + acceptVerificationRequestUnderlyingCallsCount = newValue + } + } + } + } + var acceptVerificationRequestCalled: Bool { + return acceptVerificationRequestCallsCount > 0 + } + + var acceptVerificationRequestUnderlyingReturnValue: Result! + var acceptVerificationRequestReturnValue: Result! { + get { + if Thread.isMainThread { + return acceptVerificationRequestUnderlyingReturnValue + } else { + var returnValue: Result? = nil + DispatchQueue.main.sync { + returnValue = acceptVerificationRequestUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + acceptVerificationRequestUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + acceptVerificationRequestUnderlyingReturnValue = newValue + } + } + } + } + var acceptVerificationRequestClosure: (() async -> Result)? + + func acceptVerificationRequest() async -> Result { + acceptVerificationRequestCallsCount += 1 + if let acceptVerificationRequestClosure = acceptVerificationRequestClosure { + return await acceptVerificationRequestClosure() + } else { + return acceptVerificationRequestReturnValue + } + } //MARK: - requestVerification var requestVerificationUnderlyingCallsCount = 0 diff --git a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift index abc5a60990..ec51a42ede 100644 --- a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift @@ -16529,6 +16529,92 @@ open class SessionVerificationControllerSDKMock: MatrixRustSDK.SessionVerificati fileprivate var pointer: UnsafeMutableRawPointer! + //MARK: - acceptVerificationRequest + + open var acceptVerificationRequestThrowableError: Error? + var acceptVerificationRequestUnderlyingCallsCount = 0 + open var acceptVerificationRequestCallsCount: Int { + get { + if Thread.isMainThread { + return acceptVerificationRequestUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = acceptVerificationRequestUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + acceptVerificationRequestUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + acceptVerificationRequestUnderlyingCallsCount = newValue + } + } + } + } + open var acceptVerificationRequestCalled: Bool { + return acceptVerificationRequestCallsCount > 0 + } + open var acceptVerificationRequestClosure: (() async throws -> Void)? + + open override func acceptVerificationRequest() async throws { + if let error = acceptVerificationRequestThrowableError { + throw error + } + acceptVerificationRequestCallsCount += 1 + try await acceptVerificationRequestClosure?() + } + + //MARK: - acknowledgeVerificationRequest + + open var acknowledgeVerificationRequestSenderIdFlowIdThrowableError: Error? + var acknowledgeVerificationRequestSenderIdFlowIdUnderlyingCallsCount = 0 + open var acknowledgeVerificationRequestSenderIdFlowIdCallsCount: Int { + get { + if Thread.isMainThread { + return acknowledgeVerificationRequestSenderIdFlowIdUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = acknowledgeVerificationRequestSenderIdFlowIdUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + acknowledgeVerificationRequestSenderIdFlowIdUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + acknowledgeVerificationRequestSenderIdFlowIdUnderlyingCallsCount = newValue + } + } + } + } + open var acknowledgeVerificationRequestSenderIdFlowIdCalled: Bool { + return acknowledgeVerificationRequestSenderIdFlowIdCallsCount > 0 + } + open var acknowledgeVerificationRequestSenderIdFlowIdReceivedArguments: (senderId: String, flowId: String)? + open var acknowledgeVerificationRequestSenderIdFlowIdReceivedInvocations: [(senderId: String, flowId: String)] = [] + open var acknowledgeVerificationRequestSenderIdFlowIdClosure: ((String, String) async throws -> Void)? + + open override func acknowledgeVerificationRequest(senderId: String, flowId: String) async throws { + if let error = acknowledgeVerificationRequestSenderIdFlowIdThrowableError { + throw error + } + acknowledgeVerificationRequestSenderIdFlowIdCallsCount += 1 + acknowledgeVerificationRequestSenderIdFlowIdReceivedArguments = (senderId: senderId, flowId: flowId) + DispatchQueue.main.async { + self.acknowledgeVerificationRequestSenderIdFlowIdReceivedInvocations.append((senderId: senderId, flowId: flowId)) + } + try await acknowledgeVerificationRequestSenderIdFlowIdClosure?(senderId, flowId) + } + //MARK: - approveVerification open var approveVerificationThrowableError: Error? @@ -16649,75 +16735,6 @@ open class SessionVerificationControllerSDKMock: MatrixRustSDK.SessionVerificati try await declineVerificationClosure?() } - //MARK: - isVerified - - open var isVerifiedThrowableError: Error? - var isVerifiedUnderlyingCallsCount = 0 - open var isVerifiedCallsCount: Int { - get { - if Thread.isMainThread { - return isVerifiedUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = isVerifiedUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - isVerifiedUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - isVerifiedUnderlyingCallsCount = newValue - } - } - } - } - open var isVerifiedCalled: Bool { - return isVerifiedCallsCount > 0 - } - - var isVerifiedUnderlyingReturnValue: Bool! - open var isVerifiedReturnValue: Bool! { - get { - if Thread.isMainThread { - return isVerifiedUnderlyingReturnValue - } else { - var returnValue: Bool? = nil - DispatchQueue.main.sync { - returnValue = isVerifiedUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - isVerifiedUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - isVerifiedUnderlyingReturnValue = newValue - } - } - } - } - open var isVerifiedClosure: (() async throws -> Bool)? - - open override func isVerified() async throws -> Bool { - if let error = isVerifiedThrowableError { - throw error - } - isVerifiedCallsCount += 1 - if let isVerifiedClosure = isVerifiedClosure { - return try await isVerifiedClosure() - } else { - return isVerifiedReturnValue - } - } - //MARK: - requestVerification open var requestVerificationThrowableError: Error? diff --git a/ElementX/Sources/Mocks/SessionVerificationControllerProxyMock.swift b/ElementX/Sources/Mocks/SessionVerificationControllerProxyMock.swift index 377404e7a8..55572db7b5 100644 --- a/ElementX/Sources/Mocks/SessionVerificationControllerProxyMock.swift +++ b/ElementX/Sources/Mocks/SessionVerificationControllerProxyMock.swift @@ -16,16 +16,18 @@ extension SessionVerificationControllerProxyMock { SessionVerificationEmoji(symbol: "🏁", description: "Flag"), SessionVerificationEmoji(symbol: "🌏", description: "Globe")] - static func configureMock(callbacks: PassthroughSubject = .init(), + static func configureMock(actions: PassthroughSubject = .init(), isVerified: Bool = false, requestDelay: Duration = .seconds(1)) -> SessionVerificationControllerProxyMock { let mock = SessionVerificationControllerProxyMock() - mock.underlyingCallbacks = callbacks + mock.underlyingActions = actions + + mock.acknowledgeVerificationRequestDetailsReturnValue = .success(()) mock.requestVerificationClosure = { [unowned mock] in Task.detached { try await Task.sleep(for: requestDelay) - mock.callbacks.send(.acceptedVerificationRequest) + mock.actions.send(.acceptedVerificationRequest) } return .success(()) @@ -34,11 +36,11 @@ extension SessionVerificationControllerProxyMock { mock.startSasVerificationClosure = { [unowned mock] in Task.detached { try await Task.sleep(for: requestDelay) - mock.callbacks.send(.startedSasVerification) + mock.actions.send(.startedSasVerification) Task.detached { try await Task.sleep(for: requestDelay) - mock.callbacks.send(.receivedVerificationData(emojis)) + mock.actions.send(.receivedVerificationData(emojis)) } } @@ -48,7 +50,7 @@ extension SessionVerificationControllerProxyMock { mock.approveVerificationClosure = { [unowned mock] in Task.detached { try await Task.sleep(for: requestDelay) - mock.callbacks.send(.finished) + mock.actions.send(.finished) } return .success(()) @@ -57,7 +59,7 @@ extension SessionVerificationControllerProxyMock { mock.declineVerificationClosure = { [unowned mock] in Task.detached { try await Task.sleep(for: requestDelay) - mock.callbacks.send(.cancelled) + mock.actions.send(.cancelled) } return .success(()) @@ -66,7 +68,7 @@ extension SessionVerificationControllerProxyMock { mock.cancelVerificationClosure = { [unowned mock] in Task.detached { try await Task.sleep(for: requestDelay) - mock.callbacks.send(.cancelled) + mock.actions.send(.cancelled) } return .success(()) diff --git a/ElementX/Sources/Other/AccessibilityIdentifiers.swift b/ElementX/Sources/Other/AccessibilityIdentifiers.swift index df2c1d07d6..63eb3aa73f 100644 --- a/ElementX/Sources/Other/AccessibilityIdentifiers.swift +++ b/ElementX/Sources/Other/AccessibilityIdentifiers.swift @@ -184,6 +184,8 @@ enum A11yIdentifiers { } struct SessionVerificationScreen { + let acceptVerificationRequest = "session_verification-accept_verification_request" + let ignoreVerificationRequest = "session_verification-ignore_verification_request" let requestVerification = "session_verification-request_verification" let startSasVerification = "session_verification-start_sas_verification" let acceptChallenge = "session_verification-accept_challenge" diff --git a/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/SessionVerificationScreenCoordinator.swift b/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/SessionVerificationScreenCoordinator.swift index adbd3adb54..9741f19709 100644 --- a/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/SessionVerificationScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/SessionVerificationScreenCoordinator.swift @@ -6,14 +6,21 @@ // import Combine +import MatrixRustSDK import SwiftUI enum SessionVerificationScreenCoordinatorAction { case done } +enum SessionVerificationScreenFlow { + case initiator + case responder(details: SessionVerificationRequestDetails) +} + struct SessionVerificationScreenCoordinatorParameters { let sessionVerificationControllerProxy: SessionVerificationControllerProxyProtocol + let flow: SessionVerificationScreenFlow } final class SessionVerificationScreenCoordinator: CoordinatorProtocol { @@ -27,7 +34,8 @@ final class SessionVerificationScreenCoordinator: CoordinatorProtocol { } init(parameters: SessionVerificationScreenCoordinatorParameters) { - viewModel = SessionVerificationScreenViewModel(sessionVerificationControllerProxy: parameters.sessionVerificationControllerProxy) + viewModel = SessionVerificationScreenViewModel(sessionVerificationControllerProxy: parameters.sessionVerificationControllerProxy, + flow: parameters.flow) } // MARK: - Public diff --git a/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/SessionVerificationScreenModels.swift b/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/SessionVerificationScreenModels.swift index 4046675952..ec7d72b44f 100644 --- a/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/SessionVerificationScreenModels.swift +++ b/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/SessionVerificationScreenModels.swift @@ -11,13 +11,61 @@ enum SessionVerificationScreenViewModelAction { case finished } +enum SessionVerificationScreenViewAction { + case acceptVerificationRequest + case ignoreVerificationRequest + case requestVerification + case startSasVerification + case restart + case accept + case decline + case done +} + struct SessionVerificationScreenViewState: BindableState { - var verificationState: SessionVerificationScreenStateMachine.State = .initial + let flow: SessionVerificationScreenFlow + var verificationState: SessionVerificationScreenStateMachine.State + + var headerImageName: String { + switch verificationState { + case .initial: + return "lock" + case .acceptingVerificationRequest: + return "hourglass" + case .requestingVerification: + return "hourglass" + case .verificationRequestAccepted: + return "face.smiling" + case .startingSasVerification: + return "hourglass" + case .sasVerificationStarted: + return "hourglass" + case .cancelling: + return "hourglass" + case .acceptingChallenge: + return "hourglass" + case .decliningChallenge: + return "hourglass" + case .showingChallenge: + return "face.smiling" + case .verified: + return "checkmark.shield" + case .cancelled: + return "exclamationmark.shield" + } + } var title: String? { switch verificationState { case .initial: - return L10n.screenSessionVerificationOpenExistingSessionTitle + switch flow { + case .initiator: + return L10n.screenSessionVerificationOpenExistingSessionTitle + case .responder: + return L10n.screenSessionVerificationRequestTitle + } + case .acceptingVerificationRequest: + return L10n.screenSessionVerificationRequestTitle case .requestingVerification: return L10n.screenSessionVerificationWaitingToAcceptTitle case .verificationRequestAccepted: @@ -31,13 +79,13 @@ struct SessionVerificationScreenViewState: BindableState { case .acceptingChallenge: return L10n.screenSessionVerificationCompareEmojisTitle case .decliningChallenge: - return nil + return L10n.screenSessionVerificationCompareEmojisTitle case .verified: return L10n.commonVerificationComplete case .cancelling: return nil case .cancelled: - return L10n.commonVerificationCancelled + return L10n.commonVerificationFailed } } @@ -48,7 +96,14 @@ struct SessionVerificationScreenViewState: BindableState { var message: String { switch verificationState { case .initial: - return L10n.screenSessionVerificationOpenExistingSessionSubtitle + switch flow { + case .initiator: + return L10n.screenSessionVerificationOpenExistingSessionSubtitle + case .responder: + return L10n.screenSessionVerificationRequestSubtitle + } + case .acceptingVerificationRequest: + return L10n.screenSessionVerificationRequestSubtitle case .requestingVerification: return L10n.screenSessionVerificationWaitingToAcceptSubtitle case .verificationRequestAccepted: @@ -60,7 +115,7 @@ struct SessionVerificationScreenViewState: BindableState { case .acceptingChallenge: return L10n.screenSessionVerificationCompareEmojisSubtitle case .decliningChallenge: - return L10n.commonWaiting + return L10n.screenSessionVerificationCompareEmojisSubtitle case .cancelling: return L10n.commonWaiting case .showingChallenge: @@ -68,15 +123,7 @@ struct SessionVerificationScreenViewState: BindableState { case .verified: return L10n.screenSessionVerificationCompleteSubtitle case .cancelled: - return L10n.screenSessionVerificationCancelledSubtitle + return L10n.screenSessionVerificationFailedSubtitle } } } - -enum SessionVerificationScreenViewAction { - case requestVerification - case startSasVerification - case restart - case accept - case decline -} diff --git a/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/SessionVerificationScreenStateMachine.swift b/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/SessionVerificationScreenStateMachine.swift index 1c90d5e9d3..f3a0f00d33 100644 --- a/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/SessionVerificationScreenStateMachine.swift +++ b/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/SessionVerificationScreenStateMachine.swift @@ -13,6 +13,8 @@ class SessionVerificationScreenStateMachine { enum State: StateType { /// The initial state, before verification started case initial + /// Accepting the remote verification request + case acceptingVerificationRequest /// Waiting for verification acceptance case requestingVerification /// Verification request accepted. Waiting for start @@ -37,6 +39,8 @@ class SessionVerificationScreenStateMachine { /// Events that can be triggered on the SessionVerification state machine enum Event: EventType { + /// Accept the remote verification request + case acceptVerificationRequest /// Request verification case requestVerification /// The current verification request has been accepted @@ -69,16 +73,23 @@ class SessionVerificationScreenStateMachine { stateMachine.state } - init() { - stateMachine = StateMachine(state: .initial) + init(state: State) { + stateMachine = StateMachine(state: state) configure() } private func configure() { + stateMachine.addRoutes(event: .acceptVerificationRequest, transitions: [.initial => .acceptingVerificationRequest]) stateMachine.addRoutes(event: .requestVerification, transitions: [.initial => .requestingVerification]) - stateMachine.addRoutes(event: .didAcceptVerificationRequest, transitions: [.requestingVerification => .verificationRequestAccepted]) + + stateMachine.addRoutes(event: .didAcceptVerificationRequest, transitions: [.acceptingVerificationRequest => .verificationRequestAccepted, + .requestingVerification => .verificationRequestAccepted]) + stateMachine.addRoutes(event: .startSasVerification, transitions: [.verificationRequestAccepted => .startingSasVerification]) - stateMachine.addRoutes(event: .didFail, transitions: [.requestingVerification => .initial]) + + stateMachine.addRoutes(event: .didFail, transitions: [.requestingVerification => .initial, + .acceptingVerificationRequest => .initial]) + stateMachine.addRoutes(event: .restart, transitions: [.cancelled => .initial]) // Transitions with associated values need to be handled through `addRouteMapping` diff --git a/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/SessionVerificationScreenViewModel.swift b/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/SessionVerificationScreenViewModel.swift index b1a57333e8..7264b00414 100644 --- a/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/SessionVerificationScreenViewModel.swift +++ b/ElementX/Sources/Screens/Onboarding/SessionVerificationScreen/SessionVerificationScreenViewModel.swift @@ -12,6 +12,7 @@ typealias SessionVerificationViewModelType = StateStoreViewModel some View { + static func sessionVerificationScreen(state: SessionVerificationScreenStateMachine.State, + flow: SessionVerificationScreenFlow = .initiator) -> some View { let viewModel = SessionVerificationScreenViewModel(sessionVerificationControllerProxy: SessionVerificationControllerProxyMock.configureMock(), + flow: flow, verificationState: state) return SessionVerificationScreen(context: viewModel.context) diff --git a/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift b/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift index 36d3429c32..83b2bd23dd 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift @@ -91,6 +91,7 @@ class TimelineInteractionHandler { } } + // swiftlint:disable:next cyclomatic_complexity func handleTimelineItemMenuAction(_ action: TimelineItemMenuAction, itemID: TimelineItemIdentifier) { guard let timelineItem = timelineController.timelineItems.firstUsingStableID(itemID), let eventTimelineItem = timelineItem as? EventBasedTimelineItemProtocol else { diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 28ed38ee12..3db90f896b 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -50,6 +50,8 @@ class ClientProxy: ClientProxyProtocol { let secureBackupController: SecureBackupControllerProtocol + private(set) var sessionVerificationController: SessionVerificationControllerProxyProtocol? + private static var roomCreationPowerLevelOverrides: PowerLevels { .init(usersDefault: nil, eventsDefault: nil, @@ -157,7 +159,16 @@ class ClientProxy: ClientProxyProtocol { updateVerificationState(client.encryption().verificationState()) verificationStateListenerTaskHandle = client.encryption().verificationStateListener(listener: VerificationStateListenerProxy { [weak self] verificationState in - self?.updateVerificationState(verificationState) + guard let self else { return } + + updateVerificationState(verificationState) + + // The session verification controller requires the user's identity which + // isn't available before a keys query response. Use the verification + // state updates as an aproximation for when that happens. + Task { + await self.buildSessionVerificationControllerProxyIfPossible(verificationState: verificationState) + } }) sendQueueListenerTaskHandle = client.subscribeToSendQueueStatus(listener: SendQueueRoomErrorListenerProxy { [weak self] roomID, error in @@ -484,7 +495,8 @@ class ClientProxy: ClientProxyProtocol { func roomPreviewForIdentifier(_ identifier: String, via: [String]) async -> Result { do { let roomPreview = try await client.getRoomPreviewFromRoomId(roomId: identifier, viaServers: via) - return .success(.init(roomPreview)) + let roomPreviewInfo = try roomPreview.info() + return .success(.init(roomPreviewInfo)) } catch let error as ClientError where error.code == .forbidden { return .failure(.roomPreviewIsPrivate) } catch { @@ -561,16 +573,6 @@ class ClientProxy: ClientProxyProtocol { } } - func sessionVerificationControllerProxy() async -> Result { - do { - let sessionVerificationController = try await client.getSessionVerificationController() - return .success(SessionVerificationControllerProxy(sessionVerificationController: sessionVerificationController)) - } catch { - MXLog.error("Failed retrieving session verification controller proxy with error: \(error)") - return .failure(.sdkError(error)) - } - } - func logout() async -> URL? { do { return try await client.logout().flatMap(URL.init(string:)) @@ -728,6 +730,19 @@ class ClientProxy: ClientProxyProtocol { verificationStateSubject.send(verificationState) } + + private func buildSessionVerificationControllerProxyIfPossible(verificationState: VerificationState) async { + guard sessionVerificationController == nil, verificationState != .unknown else { + return + } + + do { + let sessionVerificationController = try await client.getSessionVerificationController() + self.sessionVerificationController = SessionVerificationControllerProxy(sessionVerificationController: sessionVerificationController) + } catch { + MXLog.error("Failed retrieving session verification controller proxy with error: \(error)") + } + } private func loadUserAvatarURLFromCache() { loadCachedAvatarURLTask = Task { @@ -1079,17 +1094,17 @@ private class SendQueueRoomErrorListenerProxy: SendQueueRoomErrorListener { } private extension RoomPreviewDetails { - init(_ roomPreview: RoomPreview) { - self = RoomPreviewDetails(roomID: roomPreview.roomId, - name: roomPreview.name, - canonicalAlias: roomPreview.canonicalAlias, - topic: roomPreview.topic, - avatarURL: roomPreview.avatarUrl.flatMap(URL.init(string:)), - memberCount: UInt(roomPreview.numJoinedMembers), - isHistoryWorldReadable: roomPreview.isHistoryWorldReadable, - isJoined: roomPreview.isJoined, - isInvited: roomPreview.isInvited, - isPublic: roomPreview.isPublic, - canKnock: roomPreview.canKnock) + init(_ roomPreviewInfo: RoomPreviewInfo) { + self = RoomPreviewDetails(roomID: roomPreviewInfo.roomId, + name: roomPreviewInfo.name, + canonicalAlias: roomPreviewInfo.canonicalAlias, + topic: roomPreviewInfo.topic, + avatarURL: roomPreviewInfo.avatarUrl.flatMap(URL.init(string:)), + memberCount: UInt(roomPreviewInfo.numJoinedMembers), + isHistoryWorldReadable: roomPreviewInfo.isHistoryWorldReadable, + isJoined: roomPreviewInfo.membership == .joined, + isInvited: roomPreviewInfo.membership == .invited, + isPublic: roomPreviewInfo.joinRule == .public, + canKnock: roomPreviewInfo.joinRule == .knock) } } diff --git a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift index 8bbf4ecb55..5a28fb6a63 100644 --- a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift +++ b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift @@ -115,6 +115,8 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol { var secureBackupController: SecureBackupControllerProtocol { get } + var sessionVerificationController: SessionVerificationControllerProxyProtocol? { get } + func isOnlyDeviceLeft() async -> Result func startSync() @@ -155,8 +157,6 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol { func setUserAvatar(media: MediaInfo) async -> Result func removeUserAvatar() async -> Result - - func sessionVerificationControllerProxy() async -> Result func deactivateAccount(password: String?, eraseData: Bool) async -> Result diff --git a/ElementX/Sources/Services/ElementCall/ElementCallService.swift b/ElementX/Sources/Services/ElementCall/ElementCallService.swift index 1a02da9826..9ca9b4a058 100644 --- a/ElementX/Sources/Services/ElementCall/ElementCallService.swift +++ b/ElementX/Sources/Services/ElementCall/ElementCallService.swift @@ -246,10 +246,8 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe func provider(_ provider: CXProvider, perform action: CXEndCallAction) { #if targetEnvironment(simulator) // This gets called for no reason on simulators, where CallKit - // isn't even supported. Ignore - return - #endif - + // isn't even supported, ignore it. + #else if let ongoingCallID { actionsSubject.send(.endCall(roomID: ongoingCallID.roomID)) } @@ -257,6 +255,7 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe tearDownCallSession(sendEndCallAction: false) action.fulfill() + #endif } // MARK: - Private diff --git a/ElementX/Sources/Services/SessionVerification/SessionVerificationControllerProxy.swift b/ElementX/Sources/Services/SessionVerification/SessionVerificationControllerProxy.swift index 4ae2052ef2..72c77e1f12 100644 --- a/ElementX/Sources/Services/SessionVerification/SessionVerificationControllerProxy.swift +++ b/ElementX/Sources/Services/SessionVerification/SessionVerificationControllerProxy.swift @@ -18,6 +18,10 @@ private class WeakSessionVerificationControllerProxy: SessionVerificationControl // MARK: - SessionVerificationControllerDelegate + func didReceiveVerificationRequest(details: MatrixRustSDK.SessionVerificationRequestDetails) { + proxy?.didReceiveVerificationRequest(details: details) + } + func didReceiveVerificationData(data: MatrixRustSDK.SessionVerificationData) { switch data { // We can handle only emojis for now @@ -54,86 +58,142 @@ class SessionVerificationControllerProxy: SessionVerificationControllerProxyProt init(sessionVerificationController: SessionVerificationController) { self.sessionVerificationController = sessionVerificationController + sessionVerificationController.setDelegate(delegate: WeakSessionVerificationControllerProxy(proxy: self)) } deinit { sessionVerificationController.setDelegate(delegate: nil) } - let callbacks = PassthroughSubject() + let actions = PassthroughSubject() + + func acknowledgeVerificationRequest(details: SessionVerificationRequestDetails) async -> Result { + MXLog.info("Acknowledging verification request") + + do { + try await sessionVerificationController.acknowledgeVerificationRequest(senderId: details.senderID, flowId: details.flowID) + return .success(()) + } catch { + MXLog.error("Failed requesting session verification with error: \(error)") + return .failure(.failedAcknowledgingVerificationRequest) + } + } + + func acceptVerificationRequest() async -> Result { + MXLog.info("Accepting verification request") + + do { + try await sessionVerificationController.acceptVerificationRequest() + return .success(()) + } catch { + MXLog.error("Failed requesting session verification with error: \(error)") + return .failure(.failedAcceptingVerificationRequest) + } + } func requestVerification() async -> Result { - sessionVerificationController.setDelegate(delegate: WeakSessionVerificationControllerProxy(proxy: self)) + MXLog.info("Requesting session verification") do { try await sessionVerificationController.requestVerification() return .success(()) } catch { + MXLog.error("Failed requesting session verification with error: \(error)") return .failure(.failedRequestingVerification) } } func startSasVerification() async -> Result { + MXLog.info("Starting SAS verification") + do { try await sessionVerificationController.startSasVerification() return .success(()) } catch { + MXLog.error("Failed starting SAS verification with error: \(error)") return .failure(.failedStartingSasVerification) } } func approveVerification() async -> Result { + MXLog.info("Approving verification") + do { try await sessionVerificationController.approveVerification() return .success(()) } catch { + MXLog.error("Failed approving verification with error: \(error)") return .failure(.failedApprovingVerification) } } func declineVerification() async -> Result { + MXLog.info("Declining verification") + do { try await sessionVerificationController.declineVerification() return .success(()) } catch { + MXLog.error("Failed declining verification with error: \(error)") return .failure(.failedDecliningVerification) } } func cancelVerification() async -> Result { + MXLog.info("Cancelling verification") + do { try await sessionVerificationController.cancelVerification() return .success(()) } catch { + MXLog.error("Failed cancelling verification with error: \(error)") return .failure(.failedCancellingVerification) } } // MARK: - Private + fileprivate func didReceiveVerificationRequest(details: MatrixRustSDK.SessionVerificationRequestDetails) { + MXLog.info("Received verification request \(details)") + + let details = SessionVerificationRequestDetails(senderID: details.senderId, + flowID: details.flowId, + deviceID: details.deviceId, + displayName: details.displayName, + firstSeenDate: Date(timeIntervalSince1970: TimeInterval(details.firstSeenTimestamp / 1000))) + + actions.send(.receivedVerificationRequest(details: details)) + } + fileprivate func didAcceptVerificationRequest() { - callbacks.send(.acceptedVerificationRequest) + MXLog.info("Accepted verification request") + + actions.send(.acceptedVerificationRequest) } fileprivate func didStartSasVerification() { - callbacks.send(.startedSasVerification) + MXLog.info("Started SAS verification") + + actions.send(.startedSasVerification) } fileprivate func didReceiveData(_ data: [MatrixRustSDK.SessionVerificationEmoji]) { - callbacks.send(.receivedVerificationData(data.map { emoji in + MXLog.info("Received verification data") + + actions.send(.receivedVerificationData(data.map { emoji in SessionVerificationEmoji(symbol: emoji.symbol(), description: emoji.description()) })) } fileprivate func didFail() { - callbacks.send(.failed) + actions.send(.failed) } fileprivate func didFinish() { - callbacks.send(.finished) + actions.send(.finished) } fileprivate func didCancel() { - callbacks.send(.cancelled) + actions.send(.cancelled) } } diff --git a/ElementX/Sources/Services/SessionVerification/SessionVerificationControllerProxyProtocol.swift b/ElementX/Sources/Services/SessionVerification/SessionVerificationControllerProxyProtocol.swift index 9d1e95e22c..8f2555245b 100644 --- a/ElementX/Sources/Services/SessionVerification/SessionVerificationControllerProxyProtocol.swift +++ b/ElementX/Sources/Services/SessionVerification/SessionVerificationControllerProxyProtocol.swift @@ -7,8 +7,11 @@ import Combine import Foundation +import MatrixRustSDK enum SessionVerificationControllerProxyError: Error { + case failedAcknowledgingVerificationRequest + case failedAcceptingVerificationRequest case failedRequestingVerification case failedStartingSasVerification case failedApprovingVerification @@ -16,7 +19,8 @@ enum SessionVerificationControllerProxyError: Error { case failedCancellingVerification } -enum SessionVerificationControllerProxyCallback { +enum SessionVerificationControllerProxyAction { + case receivedVerificationRequest(details: SessionVerificationRequestDetails) case acceptedVerificationRequest case startedSasVerification case receivedVerificationData([SessionVerificationEmoji]) @@ -25,6 +29,14 @@ enum SessionVerificationControllerProxyCallback { case failed } +struct SessionVerificationRequestDetails { + let senderID: String + let flowID: String + let deviceID: String + let displayName: String? + let firstSeenDate: Date +} + struct SessionVerificationEmoji: Hashable { let symbol: String let description: String @@ -36,7 +48,11 @@ struct SessionVerificationEmoji: Hashable { // sourcery: AutoMockable protocol SessionVerificationControllerProxyProtocol { - var callbacks: PassthroughSubject { get } + var actions: PassthroughSubject { get } + + func acknowledgeVerificationRequest(details: SessionVerificationRequestDetails) async -> Result + + func acceptVerificationRequest() async -> Result func requestVerification() async -> Result diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index 1d4e6ca035..01cf66c47e 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -519,7 +519,8 @@ class MockScreen: Identifiable { return navigationStackCoordinator case .sessionVerification: var sessionVerificationControllerProxy = SessionVerificationControllerProxyMock.configureMock(requestDelay: .seconds(5)) - let parameters = SessionVerificationScreenCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationControllerProxy) + let parameters = SessionVerificationScreenCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationControllerProxy, + flow: .initiator) return SessionVerificationScreenCoordinator(parameters: parameters) case .userSessionScreen, .userSessionScreenReply: let appSettings: AppSettings = ServiceLocator.shared.settings diff --git a/PreviewTests/Sources/GeneratedPreviewTests.swift b/PreviewTests/Sources/GeneratedPreviewTests.swift index c68538da00..92bc2aa7ce 100644 --- a/PreviewTests/Sources/GeneratedPreviewTests.swift +++ b/PreviewTests/Sources/GeneratedPreviewTests.swift @@ -755,6 +755,12 @@ extension PreviewTests { } } + func test_sessionVerificationRequestDetailsView() { + for preview in SessionVerificationRequestDetailsView_Previews._allPreviews { + assertSnapshots(matching: preview) + } + } + func test_sessionVerification() { for preview in SessionVerification_Previews._allPreviews { assertSnapshots(matching: preview) diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-en-GB.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-en-GB.Set-up.png index 5fba7b07ab..fee238dbd9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-en-GB.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-en-GB.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0df8343fdaca7ed2ffa9b81b493cb1a5ab896d0f2cef08f83c0721815eb6c59d -size 133003 +oid sha256:2642ca05da869cf59a8c275465b67e43db8d01f7d984543ee4442a1a72fee1c2 +size 140623 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-pseudo.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-pseudo.Set-up.png index f61b87852d..f1c54e3960 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-pseudo.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-pseudo.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0511b9ba854183d086141e097e2cb88e39af7225d235b136e23676dde7447e9b -size 179038 +oid sha256:77d646f5b1e3f8377c674eb61b184670ba3e6e5eb26e16644a1271ec75d79cbb +size 194850 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-en-GB.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-en-GB.Set-up.png index 9da5b29f65..91b57faa30 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-en-GB.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-en-GB.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e02892489581a9b285085fcda484561ff2f88e552934d6fdf3b78c9e53d8d04 -size 88970 +oid sha256:57ba61f6ee8a7d6a382851bfe161d922a8179a58d4ddd191a08eb9891ab8a30b +size 98103 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-pseudo.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-pseudo.Set-up.png index bd7a7f31a7..b8bc03f323 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-pseudo.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-pseudo.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9ad705443a5b1fdb27deae753c0fc4ffdddfa24316c6934b1e9ba1c9a2c250a -size 137811 +oid sha256:ea9ca53d25dd7f8fbd4453bca7dc02ffde0fd5bc43c0f183489080c5804fbc7c +size 153579 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Generating.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Generating.png index a4c309c223..38f0f60eac 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Generating.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Generating.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42456c30e4c48f49498df5fb78543864ba73475b0519bb845fd8aea2b2711d2e -size 122723 +oid sha256:ffd720a584f845b15c8174adddb75e35f9e6351e765a70bd014d7ed56b49b6ca +size 122368 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Not-set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Not-set-up.png index 0513d874d1..ef9425c109 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Not-set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-en-GB.Not-set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d812c0830f2b0b88db0eba82fcbceb9b4c5698e7c55ad0e9e7fc5a9c83e96249 -size 123915 +oid sha256:f2c1eae53b77cd9dea156c19c53fbbfe28686b79d598ef81c462eccf7009dfa2 +size 123564 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Generating.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Generating.png index 3233c200c5..23191e27d3 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Generating.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Generating.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af3a401be4780e063d611293d06988524e07ea45bd4558d51d0f4f0a937ce531 -size 150029 +oid sha256:27c5dcb193ec709f28df822e0caf323f2ec813cbb04eee7e8c92f97066a46f5f +size 149956 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Not-set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Not-set-up.png index b01b7ec750..ed1dc72b25 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Not-set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPad-pseudo.Not-set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1500a5f539dc2ec3faae11c0d5a707f4329a15814d686de570baa901e1420fdf -size 151948 +oid sha256:624079d169186d352d6bc540df12a7b3f161d0653dc6659048027eef71f1e0d1 +size 151879 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Generating.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Generating.png index ed38a1f77d..84ff377f9f 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Generating.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Generating.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1b0c218ffb7143d8da4567a02fa0741a94942449dd00c065b7415978a100e6d -size 76017 +oid sha256:f93b129a941aa558325a7124f9a72ca4c6a722d67b6325cdb38f6b0c64a6af6b +size 76045 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Not-set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Not-set-up.png index 17447ad8cd..f725a97cdc 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Not-set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-en-GB.Not-set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ba57bc91e2ca37583848b7127ebb0e6efc9f932cee136e596af6bd9f762490b -size 77531 +oid sha256:9d8b72b3b29a6d7ae5302fbff84c09e5364fc28f683fbc1d917a20a213accc06 +size 77564 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Generating.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Generating.png index da61400cbf..017d38c6e5 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Generating.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Generating.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3dee45d086663e3348733885a809a68e3f86cc33c70ca4e49676daa04f8dcd02 -size 100956 +oid sha256:ccd9db4b98e04771c641c09e7d8c2bedcffd1eace9f2cb50628ac77051f81cd9 +size 101454 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Not-set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Not-set-up.png index 633281d707..8899758d0f 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Not-set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupRecoveryKeyScreen-iPhone-16-pseudo.Not-set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d6506e821995ed525758a557e0dc64c57c3331267c37cd4bcea742fc244cc435 -size 106296 +oid sha256:ce1816cf9eef04e5435a8a74c35d61fc8c8b5d4493c4577bad8bc267a0b00ea1 +size 106794 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Accepting-Challenge.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Accepting-Challenge.png new file mode 100644 index 0000000000..58c12218f7 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Accepting-Challenge.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78da542fab77c7138034ee29b3985d0b6198cb79600ec22679a9aa8cf6b7de7b +size 161895 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Accepting-Verification-Request.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Accepting-Verification-Request.png new file mode 100644 index 0000000000..e1b75aa3fb --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Accepting-Verification-Request.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f1e631e093fcbae1de84ecf012e2dad0feace858d4825f839adc9feae8d497ef +size 96902 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Cancelled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Cancelled.png index 56240ba36d..e9017e415a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Cancelled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Cancelled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94e76332e1728aac71a9684ee484eae180e84c88ea616798df0c5d883ac1e644 -size 93529 +oid sha256:5e2aacff101d124325e3966e44c22fa02ef1a307c23a723d6622d4ee64412c21 +size 95267 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Declining-Challenge.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Declining-Challenge.png new file mode 100644 index 0000000000..58c12218f7 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Declining-Challenge.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78da542fab77c7138034ee29b3985d0b6198cb79600ec22679a9aa8cf6b7de7b +size 161895 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Initial.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Initial-Initiator.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Initial.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Initial-Initiator.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Initial-Responder.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Initial-Responder.png new file mode 100644 index 0000000000..5eda7c579a --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Initial-Responder.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de6d5e5d95b4fee23d4b0a7fb771752ac426c6bf81499b9c6e3a357a10d2c2f8 +size 125144 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Requesting-Verification.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Requesting-Verification.png index ac07559283..8eed329f39 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Requesting-Verification.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Requesting-Verification.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a87fcdb4512157795aeaf16e9a94cab6a18677687f0015edbc0a696fc8a5215 -size 95084 +oid sha256:b6b0a9b619f1dc6f02399aa4976c3dc19796d987e2a41ed37e5c2a449c85c11d +size 98780 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.SAS-Verification-started.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.SAS-Verification-started.png new file mode 100644 index 0000000000..93aa6d0e38 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.SAS-Verification-started.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:356ec08a1adbca9186d746bb84c3597ec1f7e324bcc975186b9233ee3d3813d5 +size 72478 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Showing-Challenge.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Showing-Challenge.png index 79a6ef1996..1728de5221 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Showing-Challenge.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Showing-Challenge.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:90f39070e7c681d4bce49d494fe4cd967092f68dd16767db450d275993ded1cc -size 165443 +oid sha256:c8741e91150e57869e2f8d94a13360e57b4d243e9054711d2335a514e5d9029b +size 165427 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Starting-SAS-Verification.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Starting-SAS-Verification.png new file mode 100644 index 0000000000..93aa6d0e38 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-en-GB.Starting-SAS-Verification.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:356ec08a1adbca9186d746bb84c3597ec1f7e324bcc975186b9233ee3d3813d5 +size 72478 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Accepting-Challenge.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Accepting-Challenge.png new file mode 100644 index 0000000000..0dbc6de5cd --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Accepting-Challenge.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:28e89a7b00f5b07cc1ebef0034a38d2bfebdce8d8e924c20252877d8f4643d44 +size 180153 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Accepting-Verification-Request.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Accepting-Verification-Request.png new file mode 100644 index 0000000000..a372662b16 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Accepting-Verification-Request.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:55e04390c635595b18a60905e2ebffef065d27a88023484cd1a25e9a683ea378 +size 108580 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Cancelled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Cancelled.png index ac4b4d3867..877166a2c8 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Cancelled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Cancelled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec94f9759e1a8f8bba3feb88d0e35fceff8f4bd69b3860034cfd3596310f02cc -size 105774 +oid sha256:2c313669307a1f85aef0d367a1284685eee2ed0f07b4b0fcedfa3728f27b869f +size 108998 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Declining-Challenge.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Declining-Challenge.png new file mode 100644 index 0000000000..0dbc6de5cd --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Declining-Challenge.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:28e89a7b00f5b07cc1ebef0034a38d2bfebdce8d8e924c20252877d8f4643d44 +size 180153 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Initial.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Initial-Initiator.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Initial.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Initial-Initiator.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Initial-Responder.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Initial-Responder.png new file mode 100644 index 0000000000..7f1fd82499 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Initial-Responder.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bad67fb36d792965b12fe728fae7c9664d4b8031c554117162fadd4b74502045 +size 146678 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Requesting-Verification.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Requesting-Verification.png index 54c3f0007e..afa704e8d8 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Requesting-Verification.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Requesting-Verification.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:802b547e9461a061bc51df508d8474d59d0175f35da057469dbfa2e8b18414ae -size 106580 +oid sha256:fa39a6dc77a211f757f20bdfa63ef04bfd79dd3d3b69a5bb8e3d9fc454a18e06 +size 111330 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.SAS-Verification-started.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.SAS-Verification-started.png new file mode 100644 index 0000000000..f577b869db --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.SAS-Verification-started.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21a964833655bb12bbbb2ae486961cad6f3046bd3c2c6a2127c99821b14e0dcd +size 72175 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Showing-Challenge.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Showing-Challenge.png index a4f937942c..426bb9d736 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Showing-Challenge.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Showing-Challenge.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b083b28e3dd51259f7fcae01f7bd482c684b3458d30dc464aba0d4cc4094b51 -size 184847 +oid sha256:f25fc8b1b70ac00f2eb1cf5b6e55c4c3b5e509b3ed2e03458c99c36fa5abb30d +size 184353 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Starting-SAS-Verification.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Starting-SAS-Verification.png new file mode 100644 index 0000000000..f577b869db --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPad-pseudo.Starting-SAS-Verification.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21a964833655bb12bbbb2ae486961cad6f3046bd3c2c6a2127c99821b14e0dcd +size 72175 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Accepting-Challenge.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Accepting-Challenge.png new file mode 100644 index 0000000000..99147967f7 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Accepting-Challenge.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d039980194a63eb87c15b4179b6132cb98cd4b9c069a90483cc2761d771c7c6 +size 112841 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Accepting-Verification-Request.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Accepting-Verification-Request.png new file mode 100644 index 0000000000..19feb7d545 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Accepting-Verification-Request.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8df3040ac64296541ce088a3c89d173874981fb413ec12168d4e6c976364c4a +size 54304 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Cancelled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Cancelled.png index 8f36925c0d..ac0a568b0f 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Cancelled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Cancelled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:40738872c62954bd5f86d4241f3128472c8328c3295fd3f6f46bfff2c38717f4 -size 52459 +oid sha256:d3eee192ba2ba7f7a04dc6b30ebd453ff037fd93cbdbc5f7385749f598718d2c +size 52650 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Declining-Challenge.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Declining-Challenge.png new file mode 100644 index 0000000000..99147967f7 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Declining-Challenge.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d039980194a63eb87c15b4179b6132cb98cd4b9c069a90483cc2761d771c7c6 +size 112841 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Initial.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Initial-Initiator.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Initial.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Initial-Initiator.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Initial-Responder.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Initial-Responder.png new file mode 100644 index 0000000000..e8d110ad6d --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Initial-Responder.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0b4ab3956d6cdaab31410c633ad87f68b95992ed7f2f1fdcfe47d264833d925a +size 76508 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Requesting-Verification.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Requesting-Verification.png index 683e7ef272..f69522ee80 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Requesting-Verification.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Requesting-Verification.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b796634f5f0071e70fadfa6b13df5cc06425a42b450af365e91a4e14c0b6636 -size 52007 +oid sha256:c24f2aeb174dbaad526a45ea0aadc4a88e96af5acee0395dce735437f33fed37 +size 56314 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.SAS-Verification-started.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.SAS-Verification-started.png new file mode 100644 index 0000000000..c19a7629e7 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.SAS-Verification-started.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6f9ffc43e63a49254324af496f8aadda255e84f5ec97aae0f1a05542c8c1a5b +size 32123 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Showing-Challenge.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Showing-Challenge.png index 8c0cbbd04e..fd6ea1a9c9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Showing-Challenge.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Showing-Challenge.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:71eabdfd2d5cc1a86eb42043d1aa5e2a1c401722ecacdab7fd70e45898051c2c -size 114898 +oid sha256:d0c95e9f0f51109b8fef6f771a8fe27bcd33eac2e244eefbcad2f223c8228fc2 +size 115566 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Starting-SAS-Verification.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Starting-SAS-Verification.png new file mode 100644 index 0000000000..c19a7629e7 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-en-GB.Starting-SAS-Verification.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6f9ffc43e63a49254324af496f8aadda255e84f5ec97aae0f1a05542c8c1a5b +size 32123 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Accepting-Challenge.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Accepting-Challenge.png new file mode 100644 index 0000000000..bec410d878 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Accepting-Challenge.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:251fc57d77b23ca84e5b659dfb3cf51effba91e496566447dd5d5956f19eb81a +size 134250 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Accepting-Verification-Request.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Accepting-Verification-Request.png new file mode 100644 index 0000000000..20dc47e8d0 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Accepting-Verification-Request.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61faed09ca05772707dc737fe7c9fbe7c59ddd5852b607f7df13a43071d1e82c +size 74935 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Cancelled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Cancelled.png index d56b1dde99..4746b79354 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Cancelled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Cancelled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c6060f6ce1cd9d447f9d642dd2a007f1ddea9d56b877a567691f885e742e5aa -size 71244 +oid sha256:4cb1087bf68fc7dc34b5c26653ee7ddf3859f2f1de22df1513c1d7b944d68feb +size 71714 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Declining-Challenge.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Declining-Challenge.png new file mode 100644 index 0000000000..bec410d878 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Declining-Challenge.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:251fc57d77b23ca84e5b659dfb3cf51effba91e496566447dd5d5956f19eb81a +size 134250 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Initial.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Initial-Initiator.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Initial.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Initial-Initiator.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Initial-Responder.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Initial-Responder.png new file mode 100644 index 0000000000..9f49db15d9 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Initial-Responder.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2a7ffbb91030a6ba3d0e064bd6cbaaa0f109915cc4f04ceced27b0185b6a541f +size 101933 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Requesting-Verification.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Requesting-Verification.png index 644fc5b68b..d3e5135cbe 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Requesting-Verification.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Requesting-Verification.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c1f50bc1cd49c71bed1e48f540497f82fca9e5ae2130114ed2257e42065a9f9 -size 69054 +oid sha256:c40c972bb56a40bbb70725248eee5ff642e9cced47e1b794128f1c2468309a84 +size 79541 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.SAS-Verification-started.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.SAS-Verification-started.png new file mode 100644 index 0000000000..2d563d6a18 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.SAS-Verification-started.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1cd1abe33be884130a8475ada0aab30cadd3ce7b1262e316bd0b4552aacead14 +size 33085 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Showing-Challenge.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Showing-Challenge.png index ddf00c2f41..fb252261b3 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Showing-Challenge.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Showing-Challenge.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36baf8914073371e25859a9dcd82bd9e461efebc4a8905fea7b28eb1da84ae08 -size 135368 +oid sha256:91e8118f1315c91e4c4968fd6662253f0da5785dbf54a1c32aeb1b91a1785de9 +size 134584 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Starting-SAS-Verification.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Starting-SAS-Verification.png new file mode 100644 index 0000000000..2d563d6a18 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerification-iPhone-16-pseudo.Starting-SAS-Verification.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1cd1abe33be884130a8475ada0aab30cadd3ce7b1262e316bd0b4552aacead14 +size 33085 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerificationRequestDetailsView-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerificationRequestDetailsView-iPad-en-GB.1.png new file mode 100644 index 0000000000..0b931ab123 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerificationRequestDetailsView-iPad-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:db9bed799aae9732c42fc1361e72064f0b25e90abb4d8d07ec9cdc8441c8481e +size 92806 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerificationRequestDetailsView-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerificationRequestDetailsView-iPad-pseudo.1.png new file mode 100644 index 0000000000..52be29cdbb --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerificationRequestDetailsView-iPad-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d9d00be62424b27994eff708c279596bd6e3e64cdf733c9e350a4c21247db4c +size 101514 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerificationRequestDetailsView-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerificationRequestDetailsView-iPhone-16-en-GB.1.png new file mode 100644 index 0000000000..c96cbab3f8 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerificationRequestDetailsView-iPhone-16-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c77f8338cafa5cb5f14fdc64e9855a78088bffc7d0017ccea69035417532693b +size 50064 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerificationRequestDetailsView-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerificationRequestDetailsView-iPhone-16-pseudo.1.png new file mode 100644 index 0000000000..701f2bdc1f --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_sessionVerificationRequestDetailsView-iPhone-16-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe6709b8a05591a74557ed266f980dfb9c8fcfd7b4d2ed94b5cd35dd692dd45e +size 59494 diff --git a/UITests/Sources/__Snapshots__/Application/createRoom-1-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/createRoom-1-iPad-10th-generation-en-GB.UI.png index fe99073c6f..8ae26267ca 100644 --- a/UITests/Sources/__Snapshots__/Application/createRoom-1-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/createRoom-1-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f3849e4c3756976f3a618f5e865abbf3541222785b40c8da614c788c83cb4d9 -size 139468 +oid sha256:c41e07b1476f8742d3d0bf6eb63bf03942715d40437082b9a3c6cb322742e860 +size 136314 diff --git a/UITests/Sources/__Snapshots__/Application/createRoom-1-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/createRoom-1-iPhone-16-en-GB.UI.png index 0f94ddfcd9..8e531cb362 100644 --- a/UITests/Sources/__Snapshots__/Application/createRoom-1-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/createRoom-1-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b2d5bc69c3f0391f9981acb4f01bc327e4bf3a03a0a004185b343b725201b96 -size 177408 +oid sha256:2616b3ce1d12360d6c2d151d0647989bdd1ee452ea2efa6c7a24c25e8382c136 +size 167378 diff --git a/UITests/Sources/__Snapshots__/Application/createRoom-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/createRoom-iPad-10th-generation-en-GB.UI.png index 41d1da1a82..971eb34596 100644 --- a/UITests/Sources/__Snapshots__/Application/createRoom-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/createRoom-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86df341a29d0b0f1a284cc28f280d6c54d8d539f209270cf3fcc5e0b3b3da613 -size 143371 +oid sha256:8e0ace29cb70b56c0a4b23a44fa2a019036a6c29b8e5fcf5e72aa04c1e9cfeaa +size 138956 diff --git a/UITests/Sources/__Snapshots__/Application/createRoom-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/createRoom-iPhone-16-en-GB.UI.png index d9e0343b52..6a134b2a74 100644 --- a/UITests/Sources/__Snapshots__/Application/createRoom-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/createRoom-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd4e597b19ea7d5132e91427ec09ec980f4a485aea58f219a560c82003280e0a -size 182727 +oid sha256:40a328061699a3e9435f799bf20b009eaa48f66f6028037804f0435b16501fd5 +size 172709 diff --git a/UITests/Sources/__Snapshots__/Application/createRoomNoUsers-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/createRoomNoUsers-iPad-10th-generation-en-GB.UI.png index fdd758a026..3cc86974e6 100644 --- a/UITests/Sources/__Snapshots__/Application/createRoomNoUsers-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/createRoomNoUsers-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86b80fb5bb11af1953261fcbe366b85d5d6486e6860884f546a8541212cda0f1 -size 122770 +oid sha256:7be6325cac7408df468f547a3cc1ef241c24efb652160fde4150e97109b2753c +size 119761 diff --git a/UITests/Sources/__Snapshots__/Application/createRoomNoUsers-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/createRoomNoUsers-iPhone-16-en-GB.UI.png index 5953abdc1c..4bfa63870e 100644 --- a/UITests/Sources/__Snapshots__/Application/createRoomNoUsers-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/createRoomNoUsers-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49d38ecb60a832ba39029a4e8f211439aa4c3948112fbdfd51e71538bb1d0fd1 -size 155107 +oid sha256:9ab4ce7eee2d029601e4d45b92aaac783df97a9b3ecb80b208a3d9cb2e9a1ce0 +size 144339 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-0-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-0-iPad-10th-generation-en-GB.UI.png index e322f42980..e5abbd6329 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-0-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-0-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08b4681246a562c90a02a189663a7246fe135d037a7e5a0a1f5b4713a420fea1 -size 86484 +oid sha256:c548364f9ad63a7d7c1d53e53600572c3e1477b3fa1ea13d4b941a73cda76a8a +size 85908 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-1-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-1-iPad-10th-generation-en-GB.UI.png index bfd9392d6c..268b46e9d6 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-1-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-1-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ab61d812816d065aa8903c69df0459bba1142721cc9eef9e39ba05f7f505b66 -size 85240 +oid sha256:ccf7b17e2bad6bbce72d5b29e32a309865b2b2b9cb76a5af2447b675613b14c4 +size 89009 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-1-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-1-iPhone-16-en-GB.UI.png index 5fbf6aa90b..90dc376ddf 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-1-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-1-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3df7a0c44ac8ceeb5509c03f882ccaa57b5bb7fb6d54fa7f7f0ddb2c5be6d6ab -size 87190 +oid sha256:7970d3cce906d1e1947407d7a9de5e8994069bd2b191ef3dc7c0a6b6b889d3bf +size 93417 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-2-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-2-iPad-10th-generation-en-GB.UI.png index c68a1624dc..2bc9fada1e 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-2-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-2-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21932c55d7c3e55ce291c1ea410011a2b1b9647cd4e90d9295cefb85e74e8025 -size 84710 +oid sha256:8b375d9b216880cd6feea614a8ae9f722d1705e31e443d6c1620c20edb4a1a89 +size 83942 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-2-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-2-iPhone-16-en-GB.UI.png index 50cbef65e1..b48b291b3a 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-2-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-2-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e91b2715da11e04376bf99a910f9b441aa3e21d7fc9e4567b0667c84f8e85b3d -size 83835 +oid sha256:bf16f963d57f00a55133201b3fadd5820dc22d9af158d0d2163b088b675fe918 +size 84194 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-3-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-3-iPad-10th-generation-en-GB.UI.png index 8d9fc3b388..29a661411f 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-3-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-3-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b58d6f9e5d1a4cc6d337634f96e2731fa9dea1cb128e2634d2ff2dc5858eb98b -size 65167 +oid sha256:cd87a89399242622d8176b1f4eb43f72b1f51314b0b8a01f0b5ac060516e65b3 +size 64449 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-3-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-3-iPhone-16-en-GB.UI.png index 77894f7b98..5645d68add 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-3-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-3-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9728cb4db3d31f4b4c2a718c84cc8524aa18c4abdc5a7557099c82bd92c50726 -size 57405 +oid sha256:f7f4dd44b1ca63ee19ab316d3dfa4da2fa3dc65bbc875a94659074b7d454d4bb +size 56996 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-4-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-4-iPad-10th-generation-en-GB.UI.png index 0f9f266034..7ca009ad61 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-4-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-4-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d9c8b24cf3351eb186348ac79da58bbd3d54682cb37084b5f923887e8bdf8ed -size 148162 +oid sha256:d6dbe259250888fe47d35e40c377a88032e4cde2d1fdc5cbe7395afdd304cbae +size 147532 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-4-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-4-iPhone-16-en-GB.UI.png index 50598b817f..c9bdb57b5f 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-4-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-4-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78ba7b00929594e231aea48863e5224e864717e35a319a573d73af850fed6a08 -size 199952 +oid sha256:fe8a672f763bde80b901e4e8a0074658850942ed4f48e819b60bd67fef8bd025 +size 200840 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-5-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-5-iPad-10th-generation-en-GB.UI.png index c0da179fee..2d75cee457 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-5-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-5-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:67c73fd1862c07dc1d93b0e4b98d13703a01b54a268d6352fe7776d0c58376f4 -size 146945 +oid sha256:050893bf116964c98d46d78d850cbcbfd92d11d612e5018c291f23d0ab4fffdd +size 144601 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-5-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-5-iPhone-16-en-GB.UI.png index 2aba6c0bb2..4b33967bf6 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-5-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-5-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3e372a52fe24ebede306c07107f482426b7ae2a8b8ef6c9e6a8a85af433b7a5 -size 197983 +oid sha256:9651e8d3e5ccc6f2a0ea0da2b4054f901ef28de0e2175ecb457d12ac318be64a +size 195662 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-6-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-6-iPad-10th-generation-en-GB.UI.png index fb67410bd6..133318a2f6 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-6-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-6-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2bff344e5fbc9b9ccb88b22866c511cd51cc6970127955947a54b33dfdcaa5da -size 85667 +oid sha256:35a2456bd43b0c5f15392cf37bad123ac10ca4362bde64e6a096f77be597d67e +size 84840 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-7-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-7-iPad-10th-generation-en-GB.UI.png index e087e3cc4a..5b13865b03 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-7-iPad-10th-generation-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-7-iPad-10th-generation-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a154db9b1f771604817d54cc804309eaf80b92ec44632225c11cc9118f2ee6cb -size 85038 +oid sha256:3d0d342544d63dd9df0dd3f94e2a40d3bca7d9b91227e8b8b4e883efef85f19a +size 86265 diff --git a/UITests/Sources/__Snapshots__/Application/sessionVerification-7-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/sessionVerification-7-iPhone-16-en-GB.UI.png index b10bda294d..a0dd6b4208 100644 --- a/UITests/Sources/__Snapshots__/Application/sessionVerification-7-iPhone-16-en-GB.UI.png +++ b/UITests/Sources/__Snapshots__/Application/sessionVerification-7-iPhone-16-en-GB.UI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0714f4889baa952ec9597be6ab56c5aef38a706ee80bc881fd395d46f4bf958b -size 87063 +oid sha256:5c59e4b368e0bcb294803118a66eb88f6bdc51460067cbd2527ac0c878a094e5 +size 87335 diff --git a/UnitTests/Sources/SessionVerificationStateMachineTests.swift b/UnitTests/Sources/SessionVerificationStateMachineTests.swift index 472c86549c..73fe848201 100644 --- a/UnitTests/Sources/SessionVerificationStateMachineTests.swift +++ b/UnitTests/Sources/SessionVerificationStateMachineTests.swift @@ -15,7 +15,7 @@ class SessionVerificationStateMachineTests: XCTestCase { @MainActor override func setUpWithError() throws { - stateMachine = SessionVerificationScreenStateMachine() + stateMachine = SessionVerificationScreenStateMachine(state: .initial) } func testAcceptChallenge() { diff --git a/UnitTests/Sources/SessionVerificationViewModelTests.swift b/UnitTests/Sources/SessionVerificationViewModelTests.swift index c19c2df466..ec284f712a 100644 --- a/UnitTests/Sources/SessionVerificationViewModelTests.swift +++ b/UnitTests/Sources/SessionVerificationViewModelTests.swift @@ -18,7 +18,7 @@ class SessionVerificationViewModelTests: XCTestCase { override func setUpWithError() throws { sessionVerificationController = SessionVerificationControllerProxyMock.configureMock() - viewModel = SessionVerificationScreenViewModel(sessionVerificationControllerProxy: sessionVerificationController) + viewModel = SessionVerificationScreenViewModel(sessionVerificationControllerProxy: sessionVerificationController, flow: .initiator) context = viewModel.context } @@ -66,7 +66,7 @@ class SessionVerificationViewModelTests: XCTestCase { let waitForAcceptance = XCTestExpectation(description: "Wait for acceptance") - let cancellable = sessionVerificationController.callbacks + let cancellable = sessionVerificationController.actions .delay(for: .seconds(0.1), scheduler: DispatchQueue.main) // Allow the view model to process the callback first. .sink { callback in switch callback { @@ -94,7 +94,7 @@ class SessionVerificationViewModelTests: XCTestCase { let expectation = XCTestExpectation(description: "Wait for cancellation") - let cancellable = sessionVerificationController.callbacks + let cancellable = sessionVerificationController.actions .delay(for: .seconds(0.1), scheduler: DispatchQueue.main) // Allow the view model to process the callback first. .sink { callback in switch callback { @@ -124,7 +124,7 @@ class SessionVerificationViewModelTests: XCTestCase { let sasVerificationStartExpectation = XCTestExpectation(description: "Wait for SaS verification start") let verificationDataReceivalExpectation = XCTestExpectation(description: "Wait for Emoji data") - let cancellable = sessionVerificationController.callbacks + let cancellable = sessionVerificationController.actions .delay(for: .seconds(0.1), scheduler: DispatchQueue.main) // Allow the view model to process the callback first. .sink { callback in switch callback { diff --git a/project.yml b/project.yml index 51f71ea1c2..0ba6976db5 100644 --- a/project.yml +++ b/project.yml @@ -59,7 +59,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/element-hq/matrix-rust-components-swift - exactVersion: 1.0.61 + exactVersion: 1.0.62 # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios From 076ef3e9141873ad6f6c19f3fc27f945a3d15eac Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 29 Oct 2024 13:58:30 +0000 Subject: [PATCH 092/114] Reapply "min macos support" (#3458) Fixing the version in ASC didn't seem to help, maybe it needs both? --- ElementX.xcodeproj/project.pbxproj | 2 ++ project.yml | 1 + 2 files changed, 3 insertions(+) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 418ce72929..13c450f2bd 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7472,6 +7472,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.6; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; + MACOSX_DEPLOYMENT_TARGET = 14.6; MARKETING_VERSION = 1.9.4; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -7548,6 +7549,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.6; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; + MACOSX_DEPLOYMENT_TARGET = 14.6; MARKETING_VERSION = 1.9.4; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; diff --git a/project.yml b/project.yml index 0ba6976db5..3e2e9dac54 100644 --- a/project.yml +++ b/project.yml @@ -11,6 +11,7 @@ options: createIntermediateGroups: true deploymentTarget: iOS: '17.6' + macOS: '14.6' groupOrdering: - order: - ElementX From c11e49f962b108b30279948437e9f30e3cf766d4 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Tue, 29 Oct 2024 15:59:22 +0000 Subject: [PATCH 093/114] Tweak the flow for setting up a recovery key. (#3463) * Remove unwanted title. * Update SecureBackupScreen strings. * Update SecureBackupRecoveryKeyScreen. * Show the recovery banner when recovery needs setting up. * Fix SecureBackupScreen title. * Fix timeout on MP4 encoding tests --- .../Localizations/en.lproj/Localizable.strings | 5 +++-- ElementX/Sources/Generated/Strings.swift | 15 ++++++--------- .../HomeScreen/HomeScreenViewModel.swift | 2 +- .../HomeScreen/View/HomeScreenContent.swift | 2 +- ...meScreenRecoveryKeyConfirmationBanner.swift | 18 ++++++++++++++---- ...ecureBackupRecoveryKeyScreenViewModel.swift | 12 ------------ .../View/SecureBackupScreen.swift | 12 ++++++------ ...overyKeyConfirmationBanner-iPad-en-GB.1.png | 3 --- ...nfirmationBanner-iPad-en-GB.Out-of-sync.png | 3 +++ ...mationBanner-iPad-en-GB.Set-up-recovery.png | 3 +++ ...veryKeyConfirmationBanner-iPad-pseudo.1.png | 3 --- ...firmationBanner-iPad-pseudo.Out-of-sync.png | 3 +++ ...ationBanner-iPad-pseudo.Set-up-recovery.png | 3 +++ ...tionBanner-iPhone-16-en-GB.Out-of-sync.png} | 0 ...nBanner-iPhone-16-en-GB.Set-up-recovery.png | 3 +++ ...ionBanner-iPhone-16-pseudo.Out-of-sync.png} | 0 ...Banner-iPhone-16-pseudo.Set-up-recovery.png | 3 +++ ...ecureBackupScreen-iPad-en-GB.Both-setup.png | 4 ++-- ...upScreen-iPad-en-GB.Key-backup-disabled.png | 4 ++-- ...Screen-iPad-en-GB.Only-key-backup-setup.png | 4 ++-- ...upScreen-iPad-en-GB.Recovery-incomplete.png | 4 ++-- ...cureBackupScreen-iPad-pseudo.Both-setup.png | 4 ++-- ...pScreen-iPad-pseudo.Key-backup-disabled.png | 4 ++-- ...creen-iPad-pseudo.Only-key-backup-setup.png | 4 ++-- ...pScreen-iPad-pseudo.Recovery-incomplete.png | 4 ++-- ...BackupScreen-iPhone-16-en-GB.Both-setup.png | 4 ++-- ...een-iPhone-16-en-GB.Key-backup-disabled.png | 4 ++-- ...n-iPhone-16-en-GB.Only-key-backup-setup.png | 4 ++-- ...een-iPhone-16-en-GB.Recovery-incomplete.png | 4 ++-- ...ackupScreen-iPhone-16-pseudo.Both-setup.png | 4 ++-- ...en-iPhone-16-pseudo.Key-backup-disabled.png | 4 ++-- ...-iPhone-16-pseudo.Only-key-backup-setup.png | 4 ++-- ...en-iPhone-16-pseudo.Recovery-incomplete.png | 4 ++-- .../MediaUploadingPreprocessorTests.swift | 8 ++++---- 34 files changed, 85 insertions(+), 77 deletions(-) delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Out-of-sync.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Set-up-recovery.png delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Out-of-sync.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Set-up-recovery.png rename PreviewTests/Sources/__Snapshots__/PreviewTests/{test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.1.png => test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Out-of-sync.png} (100%) create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Set-up-recovery.png rename PreviewTests/Sources/__Snapshots__/PreviewTests/{test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.1.png => test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Out-of-sync.png} (100%) create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Set-up-recovery.png diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 9b14f00f5e..65ee807bcd 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -115,9 +115,9 @@ "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; "banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; +"banner_set_up_recovery_content" = "Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices."; "banner_set_up_recovery_submit" = "Set up recovery"; -"banner.set_up_recovery.content" = "Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices."; -"banner.set_up_recovery.title" = "Set up recovery to protect your account"; +"banner_set_up_recovery_title" = "Set up recovery to protect your account"; "common_about" = "About"; "common_acceptable_use_policy" = "Acceptable use policy"; "common_advanced_settings" = "Advanced settings"; @@ -459,6 +459,7 @@ "screen_chat_backup_key_backup_action_enable" = "Turn on 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_disabled_error" = "Key storage must be turned on to set up recovery."; "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"; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 087a6974de..6f23eda763 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -266,8 +266,12 @@ internal enum L10n { internal static var bannerMigrateToNativeSlidingSyncForceLogoutTitle: String { return L10n.tr("Localizable", "banner_migrate_to_native_sliding_sync_force_logout_title") } /// Upgrade available internal static var bannerMigrateToNativeSlidingSyncTitle: String { return L10n.tr("Localizable", "banner_migrate_to_native_sliding_sync_title") } + /// Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices. + internal static var bannerSetUpRecoveryContent: String { return L10n.tr("Localizable", "banner_set_up_recovery_content") } /// Set up recovery internal static var bannerSetUpRecoverySubmit: String { return L10n.tr("Localizable", "banner_set_up_recovery_submit") } + /// Set up recovery to protect your account + internal static var bannerSetUpRecoveryTitle: String { return L10n.tr("Localizable", "banner_set_up_recovery_title") } /// About internal static var commonAbout: String { return L10n.tr("Localizable", "common_about") } /// Acceptable use policy @@ -1021,6 +1025,8 @@ internal enum L10n { } /// Key storage internal static var screenChatBackupKeyBackupTitle: String { return L10n.tr("Localizable", "screen_chat_backup_key_backup_title") } + /// Key storage must be turned on to set up recovery. + internal static var screenChatBackupKeyStorageDisabledError: String { return L10n.tr("Localizable", "screen_chat_backup_key_storage_disabled_error") } /// Upload keys from this device internal static var screenChatBackupKeyStorageToggleDescription: String { return L10n.tr("Localizable", "screen_chat_backup_key_storage_toggle_description") } /// Allow key storage @@ -2499,15 +2505,6 @@ internal enum L10n { /// Check UnifiedPush internal static var troubleshootNotificationsTestUnifiedPushTitle: String { return L10n.tr("Localizable", "troubleshoot_notifications_test_unified_push_title") } - internal enum Banner { - internal enum SetUpRecovery { - /// Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices. - internal static var content: String { return L10n.tr("Localizable", "banner.set_up_recovery.content") } - /// Set up recovery to protect your account - internal static var title: String { return L10n.tr("Localizable", "banner.set_up_recovery.title") } - } - } - internal enum Common { /// Copied to clipboard internal static var copiedToClipboard: String { return L10n.tr("Localizable", "common.copied_to_clipboard") } diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index b689ce6ca1..674559ef80 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -58,7 +58,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol switch (securityState.verificationState, securityState.recoveryState) { case (.verified, .disabled): state.requiresExtraAccountSetup = true - state.securityBannerMode = .none + state.securityBannerMode = .show case (.verified, .incomplete): state.requiresExtraAccountSetup = true diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenContent.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenContent.swift index 7275684ddd..68c7408b9c 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenContent.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenContent.swift @@ -130,7 +130,7 @@ struct HomeScreenContent: View { if context.viewState.slidingSyncMigrationBannerMode == .show { HomeScreenSlidingSyncMigrationBanner(context: context) } else if context.viewState.securityBannerMode == .show { - HomeScreenRecoveryKeyConfirmationBanner(context: context) + HomeScreenRecoveryKeyConfirmationBanner(requiresExtraAccountSetup: context.viewState.requiresExtraAccountSetup, context: context) } } .background(Color.compound.bgCanvasDefault) diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift index 12fb8023e3..e801e7d218 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift @@ -9,13 +9,18 @@ import Combine import SwiftUI struct HomeScreenRecoveryKeyConfirmationBanner: View { + let requiresExtraAccountSetup: Bool var context: HomeScreenViewModel.Context + var title: String { requiresExtraAccountSetup ? L10n.bannerSetUpRecoveryTitle : L10n.confirmRecoveryKeyBannerTitle } + var message: String { requiresExtraAccountSetup ? L10n.bannerSetUpRecoveryContent : L10n.confirmRecoveryKeyBannerMessage } + var actionTitle: String { requiresExtraAccountSetup ? L10n.bannerSetUpRecoverySubmit : L10n.actionContinue } + var body: some View { VStack(alignment: .leading, spacing: 16) { VStack(alignment: .leading, spacing: 4) { HStack(spacing: 16) { - Text(L10n.confirmRecoveryKeyBannerTitle) + Text(title) .font(.compound.bodyLGSemibold) .foregroundColor(.compound.textPrimary) @@ -29,12 +34,12 @@ struct HomeScreenRecoveryKeyConfirmationBanner: View { .frame(width: 12, height: 12) } } - Text(L10n.confirmRecoveryKeyBannerMessage) + Text(message) .font(.compound.bodyMD) .foregroundColor(.compound.textSecondary) } - Button(L10n.actionContinue) { + Button(actionTitle) { context.send(viewAction: .confirmRecoveryKey) } .frame(maxWidth: .infinity) @@ -52,7 +57,12 @@ struct HomeScreenRecoveryKeyConfirmationBanner_Previews: PreviewProvider, Testab static let viewModel = buildViewModel() static var previews: some View { - HomeScreenRecoveryKeyConfirmationBanner(context: viewModel.context) + HomeScreenRecoveryKeyConfirmationBanner(requiresExtraAccountSetup: true, + context: viewModel.context) + .previewDisplayName("Set up recovery") + HomeScreenRecoveryKeyConfirmationBanner(requiresExtraAccountSetup: false, + context: viewModel.context) + .previewDisplayName("Out of sync") } static func buildViewModel() -> HomeScreenViewModel { diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift index 217b27029e..3b03ac84c6 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift @@ -28,18 +28,6 @@ class SecureBackupRecoveryKeyScreenViewModel: SecureBackupRecoveryKeyScreenViewM super.init(initialViewState: .init(isModallyPresented: isModallyPresented, mode: secureBackupController.recoveryState.value.viewMode, bindings: .init())) - - secureBackupController.recoveryState - .receive(on: DispatchQueue.main) - .sink(receiveValue: { [weak self] state in - switch state { - case .settingUp: - self?.showLoadingIndicator() - default: - self?.hideLoadingIndicator() - } - }) - .store(in: &cancellables) } // MARK: - Public diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift index 040d51e781..9ed82cc553 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift @@ -28,7 +28,7 @@ struct SecureBackupScreen: View { } } .compoundList() - .navigationTitle(L10n.commonChatBackup) + .navigationTitle(L10n.commonEncryption) .navigationBarTitleDisplayMode(.inline) .alert(item: $context.alertInfo) } @@ -68,8 +68,7 @@ struct SecureBackupScreen: View { } private var keyStorageToggle: some View { - ListRow(label: .plain(title: L10n.screenChatBackupKeyStorageToggleTitle, - description: L10n.screenChatBackupKeyStorageToggleDescription), + ListRow(label: .plain(title: L10n.screenChatBackupKeyStorageToggleTitle), kind: .toggle($context.keyStorageEnabled)) .onChange(of: context.keyStorageEnabled) { _, newValue in context.send(viewAction: .keyStorageToggled(newValue)) @@ -86,7 +85,10 @@ struct SecureBackupScreen: View { iconAlignment: .top), kind: .navigationLink { context.send(viewAction: .recoveryKey) }) case .disabled: - ListRow(label: .plain(title: L10n.screenChatBackupRecoveryActionSetup), + ListRow(label: .default(title: L10n.screenChatBackupRecoveryActionSetup, + description: L10n.screenChatBackupRecoveryActionChangeDescription, + icon: \.key, + iconAlignment: .top), details: .icon(BadgeView(size: 10)), kind: .navigationLink { context.send(viewAction: .recoveryKey) }) case .incomplete: @@ -105,8 +107,6 @@ struct SecureBackupScreen: View { @ViewBuilder private var recoveryKeySectionFooter: some View { switch context.viewState.recoveryState { - case .disabled: - Text(L10n.screenChatBackupRecoveryActionSetupDescription(InfoPlistReader.main.bundleDisplayName)) case .incomplete: Text(L10n.screenChatBackupRecoveryActionConfirmDescription) default: diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.1.png deleted file mode 100644 index 095f8b7686..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:44dd3541075b625b336187423c2ef1fd7330ce3fffb644cf117f12583df4217b -size 95942 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Out-of-sync.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Out-of-sync.png new file mode 100644 index 0000000000..04511fd48f --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Out-of-sync.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a4755216723bde1c66554701adf2fefec3f6a4b2949d66ac5875a801b1cd3d8f +size 95958 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Set-up-recovery.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Set-up-recovery.png new file mode 100644 index 0000000000..217f9b59bf --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Set-up-recovery.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e84c622fbb683ac69fa5eb646de5bd340c6105cb08dddd7ab5ecae9d55299583 +size 101351 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.1.png deleted file mode 100644 index bc208b9658..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:37087b35747692f0ddea27c331644404c9785d6d992fe8c4da46b3a6148d2753 -size 110582 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Out-of-sync.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Out-of-sync.png new file mode 100644 index 0000000000..394ef0f5f0 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Out-of-sync.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2ea3d780c157fc77da9a75f73d8b482f7d23eb539a67e854dba0ba44deda6173 +size 110663 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Set-up-recovery.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Set-up-recovery.png new file mode 100644 index 0000000000..9b05eb497d --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Set-up-recovery.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:934180921dd8b9c7feeb270fcf3a6d1a2847edba5fc4ea4c820a95b60080c218 +size 119120 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Out-of-sync.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.1.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Out-of-sync.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Set-up-recovery.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Set-up-recovery.png new file mode 100644 index 0000000000..b21ba8455e --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Set-up-recovery.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aec6cef7b010741b2db19085da28d43c3a1a511a658fa957236d665d5aaf6429 +size 65400 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Out-of-sync.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.1.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Out-of-sync.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Set-up-recovery.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Set-up-recovery.png new file mode 100644 index 0000000000..9b06387962 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Set-up-recovery.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:338ad333cedcb48ce8ea5ba3573956f122e9a48fd411249f803b32c8b755a75b +size 92631 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Both-setup.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Both-setup.png index f720b5833a..38d9174fde 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Both-setup.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Both-setup.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:41207583349f9c70c02e9f37482445511c597f46d150f6a2c289f0fd731c580b -size 138178 +oid sha256:05bb5553f7cef20942a10107f5122021954f396a5e7866efdaa1fd6e110df4a1 +size 131498 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Key-backup-disabled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Key-backup-disabled.png index 7eeb523ce1..6b413c6e11 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Key-backup-disabled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Key-backup-disabled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4574b56fd9c50e2146d6675881dd544e00d9f6ca4b8fa30beed4d42160f833f1 -size 115816 +oid sha256:248c05bf396932033fdc1f2821659988b6cdde1558c4db1ff2e36c6eef9ae02d +size 109322 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Only-key-backup-setup.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Only-key-backup-setup.png index b3ac83ec0a..e0d87a4637 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Only-key-backup-setup.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Only-key-backup-setup.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0b2432ac9ec6980dfa6618409a401d3a18474c73e37f49b5e29ba3b0dd1d0da0 -size 134492 +oid sha256:b6d9b14acdfe5dc08200d630a6cf5a0ae9d2649926b6840ffd50bde4014b7805 +size 130555 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Recovery-incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Recovery-incomplete.png index 53cb7f4678..8c01901979 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Recovery-incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Recovery-incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00b7edaaa02c2ffcb38f79f31e9f265a4c3a837df76858bd6027742b250ed8fb -size 92190 +oid sha256:ff079b6cd61f2c0fb2c4a7a66dd1cdbc297b9ee0951fad8274b0d279507544ca +size 91489 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Both-setup.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Both-setup.png index be34d219c4..dad4e9295b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Both-setup.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Both-setup.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:53c980680e3a15a67fb5bea95819d42da84dac6c15d346dec0e6e79107a3f3fe -size 169146 +oid sha256:1256574201331ee413d7821b8512fe2e3253849b3f59f25c81ea9c9322380656 +size 161073 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Key-backup-disabled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Key-backup-disabled.png index 56b4e39deb..ae13453f5c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Key-backup-disabled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Key-backup-disabled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc7cb971fef0b09290cd1296b67df22d792634a0f7ff48121e2432e1541ecfff -size 133858 +oid sha256:617075b51396eb2cbaddee74e2673f0b778b04f5f9263c8314dbdde774c6a233 +size 126840 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Only-key-backup-setup.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Only-key-backup-setup.png index ac0323fc8e..dad60db573 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Only-key-backup-setup.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Only-key-backup-setup.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b08d094fbf0ca9f671a804a6894659ad9de40f10d376b1903f0449240dd0cf17 -size 163516 +oid sha256:abc0a540d895cdc05a132329f4f9893a7be01f60da1035287be8037befe46c6c +size 160021 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Recovery-incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Recovery-incomplete.png index 849dd80a4e..05a8132d7e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Recovery-incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Recovery-incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4471d33581e3ebb69e7c83f87288f9c67267e84845606066db3e3fc88813fa2a -size 95297 +oid sha256:47fe19f5a821b1ef13956f05ea55ec4f9d22ec8846a5596b318eec1cc0974071 +size 94488 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Both-setup.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Both-setup.png index 023226e679..92e1a2e94f 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Both-setup.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Both-setup.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d297f54924748f5aa9d03852553f953647b2d18437a0e598371796fd006f33f5 -size 93416 +oid sha256:c532779ed296a8cc868cde0e0b392ebb55ee385dd1210d1e08aaed591a49f6ce +size 89453 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Key-backup-disabled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Key-backup-disabled.png index cef726f648..8be7688aa1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Key-backup-disabled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Key-backup-disabled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cdef08ee53dcd8b6ba7f599e6264b17ca23add1cc0c824d5bb01ba033223920c -size 70563 +oid sha256:1203fa1008ba94be887f96b286efa557a96577cf30ea0a3f882b9f7576b4c646 +size 66212 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Only-key-backup-setup.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Only-key-backup-setup.png index 53943bc2c4..b95b0f08eb 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Only-key-backup-setup.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Only-key-backup-setup.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9cdf64b4ce3eedf3b3c9dc78adecf093d562dd839ff94b34550602592d77f6ad -size 89304 +oid sha256:a7539759e9e9bdfd354ffa771de2c6b733697203b9c11b982890554e14a9bbae +size 89693 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Recovery-incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Recovery-incomplete.png index e016c1794f..66dcae71d1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Recovery-incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Recovery-incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:efa3569bb4408168dc0416e65de3c57406505199f92e7b81ffb697242b439a14 -size 45404 +oid sha256:fb3b0d4c592e7dd54650f94bcdbf1d3763dd946c7f8c753b024a27d24e3d7cdb +size 45210 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Both-setup.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Both-setup.png index 076fe71647..dd6768c64a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Both-setup.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Both-setup.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:96b19939706086621b14c3b28dc7cc6cff5f92363f241e4efd907cbc82aa1ef1 -size 145495 +oid sha256:768a1a253229d73ddf4ce3a26576cc48c252dfcd205bf092eca7779f40d00900 +size 134673 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Key-backup-disabled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Key-backup-disabled.png index 948ff62d83..9f4565b683 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Key-backup-disabled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Key-backup-disabled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5bfb165c7566fa93c490d775c4d1ce1eed9a78bf78ad2ff7e3029e48684cca1 -size 99065 +oid sha256:9199e5915f3292565b67005b242619cf670f07024c7097ab0e7af7bd5bc23710 +size 89974 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Only-key-backup-setup.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Only-key-backup-setup.png index e988640be4..fd2cd2af72 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Only-key-backup-setup.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Only-key-backup-setup.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c08b79a27496e4c14e6a20b3dcc986cd4ac028ad9fbc1560ce331b3b03b1f9c6 -size 135286 +oid sha256:3e3b703d69dfc4c7a231b32eefe63c3fd6010d84cda71ef9b9fd2b9555a21aa6 +size 134095 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Recovery-incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Recovery-incomplete.png index d47054de82..c72a721dd3 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Recovery-incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Recovery-incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db5c48557a3e20b0767a93b92f0f3a72351f7dd1f2ca6d89afb108aac8ff73d2 -size 53699 +oid sha256:0709b19f855c68b6c72d16a6377ef96a388ffec5604c2d66492c8602cfa11e90 +size 53200 diff --git a/UnitTests/Sources/MediaUploadingPreprocessorTests.swift b/UnitTests/Sources/MediaUploadingPreprocessorTests.swift index f8742fca4c..68734fada8 100644 --- a/UnitTests/Sources/MediaUploadingPreprocessorTests.swift +++ b/UnitTests/Sources/MediaUploadingPreprocessorTests.swift @@ -46,8 +46,8 @@ final class MediaUploadingPreprocessorTests: XCTestCase { } func testLandscapeMovVideoProcessing() async { - // Allow double the default execution time as we encode the video twice now. - executionTimeAllowance = 120 + // Allow an increased execution time as we encode the video twice now. + executionTimeAllowance = 180 guard let url = Bundle(for: Self.self).url(forResource: "landscape_test_video.mov", withExtension: nil) else { XCTFail("Failed retrieving test asset") @@ -109,8 +109,8 @@ final class MediaUploadingPreprocessorTests: XCTestCase { } func testPortraitMp4VideoProcessing() async { - // Allow double the default execution time as we encode the video twice now. - executionTimeAllowance = 120 + // Allow an increased execution time as we encode the video twice now. + executionTimeAllowance = 180 guard let url = Bundle(for: Self.self).url(forResource: "portrait_test_video.mp4", withExtension: nil) else { XCTFail("Failed retrieving test asset") From 368226fd575799ca57e1cd48c05cf12f4d2c5186 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:39:55 +0000 Subject: [PATCH 094/114] Tweak the flow for disabling key storage. (#3464) * Refactor RoundedLabelItem into the Compound VisualListItem component. * Update the design of SecureBackupKeyBackupScreen. * Add a hint to the key storage toggle when disabled. --- ElementX.xcodeproj/project.pbxproj | 8 ++-- ...edLabelItem.swift => VisualListItem.swift} | 26 +++++----- .../View/EncryptionResetScreen.swift | 2 +- .../View/AnalyticsPromptScreen.swift | 2 +- .../View/SecureBackupKeyBackupScreen.swift | 48 +++++++++---------- .../SecureBackupScreenModels.swift | 4 ++ .../SecureBackupScreenViewModel.swift | 12 ++--- .../View/SecureBackupScreen.swift | 3 +- .../Sources/GeneratedPreviewTests.swift | 12 ++--- ...est_analyticsPromptScreen-iPad-en-GB.1.png | 4 +- ...st_analyticsPromptScreen-iPad-pseudo.1.png | 4 +- ...nalyticsPromptScreen-iPhone-16-en-GB.1.png | 4 +- ...alyticsPromptScreen-iPhone-16-pseudo.1.png | 4 +- ...PromptScreenCheckmarkItem-iPad-en-GB.1.png | 3 -- ...romptScreenCheckmarkItem-iPad-pseudo.1.png | 3 -- ...tScreenCheckmarkItem-iPhone-16-en-GB.1.png | 3 -- ...ScreenCheckmarkItem-iPhone-16-pseudo.1.png | 3 -- ...est_encryptionResetScreen-iPad-en-GB.1.png | 4 +- ...st_encryptionResetScreen-iPad-pseudo.1.png | 4 +- ...ncryptionResetScreen-iPhone-16-en-GB.1.png | 4 +- ...cryptionResetScreen-iPhone-16-pseudo.1.png | 4 +- ...ackupKeyBackupScreen-iPad-en-GB.Set-up.png | 4 +- ...ckupKeyBackupScreen-iPad-pseudo.Set-up.png | 4 +- ...KeyBackupScreen-iPhone-16-en-GB.Set-up.png | 4 +- ...eyBackupScreen-iPhone-16-pseudo.Set-up.png | 4 +- ...pScreen-iPad-en-GB.Key-backup-disabled.png | 4 +- ...Screen-iPad-pseudo.Key-backup-disabled.png | 4 +- ...en-iPhone-16-en-GB.Key-backup-disabled.png | 4 +- ...n-iPhone-16-pseudo.Key-backup-disabled.png | 4 +- .../test_visualListItem-iPad-en-GB.1.png | 3 ++ .../test_visualListItem-iPad-pseudo.1.png | 3 ++ .../test_visualListItem-iPhone-16-en-GB.1.png | 3 ++ ...test_visualListItem-iPhone-16-pseudo.1.png | 3 ++ 33 files changed, 105 insertions(+), 100 deletions(-) rename ElementX/Sources/Other/SwiftUI/Views/{RoundedLabelItem.swift => VisualListItem.swift} (70%) delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreenCheckmarkItem-iPad-en-GB.1.png delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreenCheckmarkItem-iPad-pseudo.1.png delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreenCheckmarkItem-iPhone-16-en-GB.1.png delete mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreenCheckmarkItem-iPhone-16-pseudo.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_visualListItem-iPad-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_visualListItem-iPad-pseudo.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_visualListItem-iPhone-16-en-GB.1.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_visualListItem-iPhone-16-pseudo.1.png diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 13c450f2bd..88da35f4a1 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -861,7 +861,6 @@ BC7CA1379D7C24F47B1B8B7E /* PaginationIndicatorRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E7F7A975514E850A834B29F /* PaginationIndicatorRoomTimelineView.swift */; }; BCC864190651B3A3CF51E4DF /* MediaFileHandleProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC1D382565A4E9CAC2F14EA /* MediaFileHandleProxy.swift */; }; BD0BE20DBCE31253AE4490A1 /* RoomListFiltersEmptyStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1DDB2293A51EA4C2739351 /* RoomListFiltersEmptyStateView.swift */; }; - BD11E639CF566A9DA8FCA717 /* RoundedLabelItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE7C80EF77AD102053D3646E /* RoundedLabelItem.swift */; }; BD6685592716CA957D7BAAC4 /* RoomChangeRolesScreenSelectedItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D9B45D584D232CB9E5C7734 /* RoomChangeRolesScreenSelectedItem.swift */; }; BD782053BE4C3D2F0BDE5699 /* ServiceLocator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57F95CADD0A5DBD76B990FCB /* ServiceLocator.swift */; }; BDA68E8D95B2B24B28825B8B /* LoginScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C368CAB3063EF275357ECD4 /* LoginScreenViewModel.swift */; }; @@ -1073,6 +1072,7 @@ EEAE954289DE813A61656AE0 /* LayoutDirection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14D83B2B7CD5501A0089EFC /* LayoutDirection.swift */; }; EEB9C1555C63B93CA9C372C2 /* EmojiPickerScreenHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B5E29E9A22F45534FBD5B58 /* EmojiPickerScreenHeaderView.swift */; }; EEC40663922856C65D1E0DF5 /* KeychainControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB9C37196A4C79F24CE80C6 /* KeychainControllerTests.swift */; }; + EED33AFD9334EFD7398707A6 /* VisualListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD529C89924EE32CE307F36F /* VisualListItem.swift */; }; EF0D0155DD104C7A41A2EB0E /* PlainMentionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE78FA0011E07920AE83135 /* PlainMentionBuilder.swift */; }; EF47D802A404A53F15D5D4B6 /* JoinRoomScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD7C0A2750998C2D77AD00F /* JoinRoomScreenViewModel.swift */; }; EF5009AC03212227131C8AF2 /* RoomNotificationSettingsProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E55B5EA766E89FF1F87C3ACB /* RoomNotificationSettingsProxyProtocol.swift */; }; @@ -1958,6 +1958,7 @@ ACD7BD6BEE21264F6677904A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = ""; }; AD0FF64B0E6470F66F42E182 /* EstimatedWaveformView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EstimatedWaveformView.swift; sourceTree = ""; }; AD378D580A41E42560C60E9C /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = ""; }; + AD529C89924EE32CE307F36F /* VisualListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualListItem.swift; sourceTree = ""; }; AD558A898847C179E4B7A237 /* SecureBackupKeyBackupScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupKeyBackupScreen.swift; sourceTree = ""; }; AD6B522BD637845AB9570B10 /* RoomNotificationSettingsProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsProxy.swift; sourceTree = ""; }; AD6E082B0507FB28F966516A /* CallNotificationRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallNotificationRoomTimelineView.swift; sourceTree = ""; }; @@ -2175,7 +2176,6 @@ DCF239C619971FDE48132550 /* SecureBackupLogoutConfirmationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreenModels.swift; sourceTree = ""; }; DD97F9661ABF08CE002054A2 /* AppLockServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockServiceTests.swift; sourceTree = ""; }; DE5127D6EA05B2E45D0A7D59 /* JoinRoomScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenViewModelTests.swift; sourceTree = ""; }; - DE7C80EF77AD102053D3646E /* RoundedLabelItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedLabelItem.swift; sourceTree = ""; }; DEC1D382565A4E9CAC2F14EA /* MediaFileHandleProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaFileHandleProxy.swift; sourceTree = ""; }; DF05DA24F71B455E8EFEBC3B /* SessionVerificationViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationViewModelTests.swift; sourceTree = ""; }; DF17EA323AD0205A6AB621AA /* Snapshotting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Snapshotting.swift; sourceTree = ""; }; @@ -2974,9 +2974,9 @@ 1627F2D56477BD331F6D732C /* RoomHeaderView.swift */, 7EB58E4E8D6D634C246AD5C2 /* RoomInviterLabel.swift */, 839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */, - DE7C80EF77AD102053D3646E /* RoundedLabelItem.swift */, AEB5FF7A09B79B0C6B528F7C /* SFNumberedListView.swift */, E10DA51DBC8C7E1460DBCCBD /* UserProfileListRow.swift */, + AD529C89924EE32CE307F36F /* VisualListItem.swift */, ); path = Views; sourceTree = ""; @@ -6856,7 +6856,6 @@ B272E5D1DE8BDA87A6B7A696 /* RoomTimelineProviderMock.swift in Sources */, 77D7DAA41AAB36800C1F2E2D /* RoomTimelineProviderProtocol.swift in Sources */, B2F8E01ABA1BA30265B4ECBE /* RoundedCornerShape.swift in Sources */, - BD11E639CF566A9DA8FCA717 /* RoundedLabelItem.swift in Sources */, 50C90117FE25390BFBD40173 /* RustTracing.swift in Sources */, D43F0503EF2CBC55272538FE /* SDKGeneratedMocks.swift in Sources */, 88CBF1595E39CE697928DE48 /* SFNumberedListView.swift in Sources */, @@ -7053,6 +7052,7 @@ 1A83DD22F3E6F76B13B6E2F9 /* VideoRoomTimelineItemContent.swift in Sources */, 2CA61BB208CD82EBDB58CD13 /* VideoRoomTimelineView.swift in Sources */, 6FC10A00D268FCD48B631E37 /* ViewFrameReader.swift in Sources */, + EED33AFD9334EFD7398707A6 /* VisualListItem.swift in Sources */, 1318721F4E5F307586D98112 /* VoiceMessageButton.swift in Sources */, 4681820102DAC8BA586357D4 /* VoiceMessageCache.swift in Sources */, 4F2DF6138E87A4B8C2488CA3 /* VoiceMessageCacheProtocol.swift in Sources */, diff --git a/ElementX/Sources/Other/SwiftUI/Views/RoundedLabelItem.swift b/ElementX/Sources/Other/SwiftUI/Views/VisualListItem.swift similarity index 70% rename from ElementX/Sources/Other/SwiftUI/Views/RoundedLabelItem.swift rename to ElementX/Sources/Other/SwiftUI/Views/VisualListItem.swift index d13e8c0aab..e84e00519b 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/RoundedLabelItem.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/VisualListItem.swift @@ -24,11 +24,11 @@ enum ListPosition { } } -struct RoundedLabelItem: View { +struct VisualListItem: View { @Environment(\.backgroundStyle) private var backgroundStyle let title: String - let listPosition: ListPosition + let position: ListPosition let iconContent: () -> Icon private var backgroundColor: AnyShapeStyle { @@ -39,17 +39,17 @@ struct RoundedLabelItem: View { Label { Text(title) } icon: { iconContent() } - .labelStyle(CheckmarkLabelStyle()) - .padding(.horizontal, 20) + .labelStyle(VisualListItemLabelStyle()) + .padding(.horizontal, 16) .padding(.vertical, 12) .frame(maxWidth: .infinity, alignment: .leading) - .background(backgroundColor, in: RoundedCornerShape(radius: 16, corners: listPosition.roundedCorners)) + .background(backgroundColor, in: RoundedCornerShape(radius: 14, corners: position.roundedCorners)) } } -private struct CheckmarkLabelStyle: LabelStyle { +private struct VisualListItemLabelStyle: LabelStyle { func makeBody(configuration: Configuration) -> some View { - HStack(alignment: .top, spacing: 16) { + HStack(alignment: .top, spacing: 12) { configuration.icon configuration.title } @@ -60,7 +60,7 @@ private struct CheckmarkLabelStyle: LabelStyle { // MARK: - Previews -struct AnalyticsPromptScreenCheckmarkItem_Previews: PreviewProvider, TestablePreview { +struct VisualListItem_Previews: PreviewProvider, TestablePreview { static let strings = AnalyticsPromptScreenStrings(termsURL: ServiceLocator.shared.settings.analyticsConfiguration.termsURL) @ViewBuilder @@ -75,17 +75,17 @@ struct AnalyticsPromptScreenCheckmarkItem_Previews: PreviewProvider, TestablePre static var previews: some View { VStack(alignment: .leading, spacing: 4) { - RoundedLabelItem(title: strings.point1, listPosition: .top) { + VisualListItem(title: strings.point1, position: .top) { testImage1 } - RoundedLabelItem(title: strings.point2, listPosition: .middle) { + VisualListItem(title: strings.point2, position: .middle) { testImage2 } - RoundedLabelItem(title: "This is a short string.", listPosition: .middle) { + VisualListItem(title: "This is a short string.", position: .middle) { testImage1 } - RoundedLabelItem(title: "This is a very long string that will be used to test the layout over multiple lines of text to ensure everything is correct.", - listPosition: .bottom) { + VisualListItem(title: "This is a very long string that will be used to test the layout over multiple lines of text to ensure everything is correct.", + position: .bottom) { testImage2 } } diff --git a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/View/EncryptionResetScreen.swift b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/View/EncryptionResetScreen.swift index bd8956493d..0bcf831683 100644 --- a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/View/EncryptionResetScreen.swift +++ b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/View/EncryptionResetScreen.swift @@ -70,7 +70,7 @@ struct EncryptionResetScreen: View { @ViewBuilder private func checkMarkItem(title: String, position: ListPosition, positive: Bool) -> some View { - RoundedLabelItem(title: title, listPosition: position) { + VisualListItem(title: title, position: position) { CompoundIcon(positive ? \.check : \.info) .foregroundColor(positive ? .compound.iconAccentPrimary : .compound.iconSecondary) .alignmentGuide(.top) { _ in 2 } diff --git a/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/View/AnalyticsPromptScreen.swift b/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/View/AnalyticsPromptScreen.swift index 08a93f44d1..a63f4ba6ef 100644 --- a/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/View/AnalyticsPromptScreen.swift +++ b/ElementX/Sources/Screens/Onboarding/AnalyticsPromptScreen/View/AnalyticsPromptScreen.swift @@ -65,7 +65,7 @@ struct AnalyticsPromptScreen: View { @ViewBuilder private func checkMarkItem(title: String, position: ListPosition) -> some View { - RoundedLabelItem(title: title, listPosition: position) { + VisualListItem(title: title, position: position) { CompoundIcon(\.checkCircle, size: .small, relativeTo: .body) .foregroundColor(.compound.iconAccentPrimary) } diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupKeyBackupScreen/View/SecureBackupKeyBackupScreen.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupKeyBackupScreen/View/SecureBackupKeyBackupScreen.swift index 63f7463b93..2f7923c01b 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupKeyBackupScreen/View/SecureBackupKeyBackupScreen.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupKeyBackupScreen/View/SecureBackupKeyBackupScreen.swift @@ -39,37 +39,37 @@ struct SecureBackupKeyBackupScreen: View { } private var disableBackupSection: some View { - VStack(spacing: 16) { - BigIcon(icon: \.keyOffSolid) - - Text(L10n.screenKeyBackupDisableTitle) - .foregroundColor(.compound.textPrimary) - .font(.compound.headingMDBold) - .multilineTextAlignment(.center) - - Text(L10n.screenKeyBackupDisableDescription) - .foregroundColor(.compound.textSecondary) - .font(.compound.bodyMD) - .multilineTextAlignment(.center) - - VStack(alignment: .leading, spacing: 10) { - Label { - Text(L10n.screenKeyBackupDisableDescriptionPoint1) + VStack(spacing: 24) { + VStack(spacing: 16) { + BigIcon(icon: \.error, style: .alertSolid) + + VStack(spacing: 8) { + Text(L10n.screenKeyBackupDisableTitle) + .foregroundColor(.compound.textPrimary) + .font(.compound.headingMDBold) + .multilineTextAlignment(.center) + + Text(L10n.screenKeyBackupDisableDescription) .foregroundColor(.compound.textSecondary) .font(.compound.bodyMD) - } icon: { - CompoundIcon(\.close) + .multilineTextAlignment(.center) + } + } + + VStack(alignment: .leading, spacing: 4) { + VisualListItem(title: L10n.screenKeyBackupDisableDescriptionPoint1, + position: .top) { + CompoundIcon(\.close, size: .small, relativeTo: .body) .foregroundColor(.compound.iconCriticalPrimary) } + .backgroundStyle(.compound.bgActionSecondaryHovered) - Label { - Text(L10n.screenKeyBackupDisableDescriptionPoint2(InfoPlistReader.main.bundleDisplayName)) - .foregroundColor(.compound.textSecondary) - .font(.compound.bodyMD) - } icon: { - CompoundIcon(\.close) + VisualListItem(title: L10n.screenKeyBackupDisableDescriptionPoint2(InfoPlistReader.main.productionAppName), + position: .bottom) { + CompoundIcon(\.close, size: .small, relativeTo: .body) .foregroundColor(.compound.iconCriticalPrimary) } + .backgroundStyle(.compound.bgActionSecondaryHovered) } } } diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenModels.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenModels.swift index 34f07c85d4..e0cf773231 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenModels.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenModels.swift @@ -17,6 +17,10 @@ struct SecureBackupScreenViewState: BindableState { var recoveryState = SecureBackupRecoveryState.unknown var keyBackupState = SecureBackupKeyBackupState.unknown var bindings: SecureBackupScreenViewStateBindings + + var keyStorageToggleDescription: String? { + keyBackupState.keyStorageToggleState ? nil : L10n.screenChatBackupKeyStorageDisabledError + } } struct SecureBackupScreenViewStateBindings { diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenViewModel.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenViewModel.swift index 3f695da2b7..6e3cf495ef 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenViewModel.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenViewModel.swift @@ -26,7 +26,7 @@ class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackup self.userIndicatorController = userIndicatorController super.init(initialViewState: .init(chatBackupDetailsURL: chatBackupDetailsURL, - bindings: SecureBackupScreenViewStateBindings(keyStorageEnabled: secureBackupController.keyBackupState.value.toggleState))) + bindings: SecureBackupScreenViewStateBindings(keyStorageEnabled: secureBackupController.keyBackupState.value.keyStorageToggleState))) secureBackupController.recoveryState .receive(on: DispatchQueue.main) @@ -38,7 +38,7 @@ class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackup .sink { [weak self] state in guard let self else { return } self.state.keyBackupState = state - self.state.bindings.keyStorageEnabled = state.toggleState + self.state.bindings.keyStorageEnabled = state.keyStorageToggleState } .store(in: &cancellables) } @@ -53,10 +53,10 @@ class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackup let keyBackupState = secureBackupController.keyBackupState.value switch (keyBackupState, enable) { case (.unknown, true): - state.bindings.keyStorageEnabled = keyBackupState.toggleState // Reset the toggle in case enabling fails + state.bindings.keyStorageEnabled = keyBackupState.keyStorageToggleState // Reset the toggle in case enabling fails enableBackup() case (.enabled, false): - state.bindings.keyStorageEnabled = keyBackupState.toggleState // Reset the toggle in case the user cancels + state.bindings.keyStorageEnabled = keyBackupState.keyStorageToggleState // Reset the toggle in case the user cancels actionsSubject.send(.keyBackup) default: break @@ -83,8 +83,8 @@ class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackup } } -private extension SecureBackupKeyBackupState { - var toggleState: Bool { +extension SecureBackupKeyBackupState { + var keyStorageToggleState: Bool { switch self { case .unknown, .enabling: false case .enabled, .disabling: true diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift index 9ed82cc553..dbdb8a2317 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift @@ -68,7 +68,8 @@ struct SecureBackupScreen: View { } private var keyStorageToggle: some View { - ListRow(label: .plain(title: L10n.screenChatBackupKeyStorageToggleTitle), + ListRow(label: .plain(title: L10n.screenChatBackupKeyStorageToggleTitle, + description: context.viewState.keyStorageToggleDescription), kind: .toggle($context.keyStorageEnabled)) .onChange(of: context.keyStorageEnabled) { _, newValue in context.send(viewAction: .keyStorageToggled(newValue)) diff --git a/PreviewTests/Sources/GeneratedPreviewTests.swift b/PreviewTests/Sources/GeneratedPreviewTests.swift index 92bc2aa7ce..c42a3a3024 100644 --- a/PreviewTests/Sources/GeneratedPreviewTests.swift +++ b/PreviewTests/Sources/GeneratedPreviewTests.swift @@ -17,12 +17,6 @@ extension PreviewTests { } } - func test_analyticsPromptScreenCheckmarkItem() { - for preview in AnalyticsPromptScreenCheckmarkItem_Previews._allPreviews { - assertSnapshots(matching: preview) - } - } - func test_analyticsPromptScreen() { for preview in AnalyticsPromptScreen_Previews._allPreviews { assertSnapshots(matching: preview) @@ -953,6 +947,12 @@ extension PreviewTests { } } + func test_visualListItem() { + for preview in VisualListItem_Previews._allPreviews { + assertSnapshots(matching: preview) + } + } + func test_voiceMessageButton() { for preview in VoiceMessageButton_Previews._allPreviews { assertSnapshots(matching: preview) diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPad-en-GB.1.png index 1ce6f753f0..77ac7a1a0b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8b702610b9a43191c4dccee0173300c5c5db9041e60ad9bc581ebfc2a47fcd8a -size 519748 +oid sha256:dfb5ef0504933e89135643a41b30ee4d70aa5ecfab0bb20253f50dab03b0db6a +size 516263 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPad-pseudo.1.png index 3257c220a6..0191f97e8a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2151c4124c4f826a9b2b1039ec2cffea1f7f9930e37918ca65558239190c8032 -size 545056 +oid sha256:95e764b2d91c03d6f6a7c40635fb14a5f2e041ff77419e8e9ef5b42a54060b43 +size 542507 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPhone-16-en-GB.1.png index fa61a7ccfb..d46b6e4b9b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76a550baf0bfc5fc2e7d2ccbfdb1e367cd025a3daca8526d4040a46523454869 -size 316889 +oid sha256:85867b67a34544e7f4e87b2f3aaf0f915f65977c478d8f2d3ba7dc42a80ab200 +size 320240 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPhone-16-pseudo.1.png index aa1e5701e0..1b5d5e3d14 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e28e24910ef83b66f273751cb0427e7f6df1ac127bfe6422ca172e7be0819bc -size 323831 +oid sha256:7c9c4b6a6b275e412de5ef313c4e435fc5786476404b421ca899a47d07cc9ebf +size 331190 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreenCheckmarkItem-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreenCheckmarkItem-iPad-en-GB.1.png deleted file mode 100644 index bca1ba570f..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreenCheckmarkItem-iPad-en-GB.1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4959f2b957a1fc21cd35b690b5eea1fc9aab9cc84fe13c3a50b3fbecea276e3c -size 110926 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreenCheckmarkItem-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreenCheckmarkItem-iPad-pseudo.1.png deleted file mode 100644 index 9547089cfb..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreenCheckmarkItem-iPad-pseudo.1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4f3d9c02c0c19490c9b1245d86dfdd97468f0cafa8c32bb9d01a62c1dccca6a9 -size 118558 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreenCheckmarkItem-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreenCheckmarkItem-iPhone-16-en-GB.1.png deleted file mode 100644 index a13dfe7638..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreenCheckmarkItem-iPhone-16-en-GB.1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7558f09b10e62a9a111e91fe259f86c3709bf354867b406b23a3a122c67d9d29 -size 73551 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreenCheckmarkItem-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreenCheckmarkItem-iPhone-16-pseudo.1.png deleted file mode 100644 index d4d76a69c9..0000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_analyticsPromptScreenCheckmarkItem-iPhone-16-pseudo.1.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2bea6d31ae7c93fb10936d2af89bdefa799d2a8d5a314c2d7e351dd817c6bf68 -size 87505 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-en-GB.1.png index 2b2c61bdf3..1a8dee1aee 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6669bd265a9b14b47f3e80786ce487275a81dd0a7c096bab707fc573d5ca5ba -size 156868 +oid sha256:8530b94b6ad2bb808589d221dd963d398a853c218641813235430f51f5d05b3c +size 155840 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-pseudo.1.png index cf3c41e801..9d7d7792db 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13eb76a6174949e63d35e260744763a405b7fd4cdfb0405189b647115be04f4d -size 212875 +oid sha256:652fe68e0477a949d5d7284de23705a87479741e55ba0ed89a40281d23c31e20 +size 212237 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-en-GB.1.png index 0e1b4d78c0..fad264c445 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2f9383e9ccde9ed8fda0461051e0c445537c299f3881b2d7feeae084bdb6be4 -size 107493 +oid sha256:67dc40a01ee89d875ae4723b8809384d5907399d536a26a520489227c3557c83 +size 108502 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-pseudo.1.png index a20c9ecf3c..570740b222 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_encryptionResetScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:359d2bfea87562d0bce3472e9994180397a2b33315b9d6768439bfbdcc3eb61a -size 164012 +oid sha256:33e757ca5012297a0f1a049f0bad15171f2043ee9224dbc148389607a15be8dd +size 163996 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-en-GB.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-en-GB.Set-up.png index fee238dbd9..f603b02320 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-en-GB.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-en-GB.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2642ca05da869cf59a8c275465b67e43db8d01f7d984543ee4442a1a72fee1c2 -size 140623 +oid sha256:9534f1649e9fdf3229867cce6594bfdf6fad971c5e3fbb5583da7980e736321a +size 144792 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-pseudo.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-pseudo.Set-up.png index f1c54e3960..27156646c2 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-pseudo.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPad-pseudo.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:77d646f5b1e3f8377c674eb61b184670ba3e6e5eb26e16644a1271ec75d79cbb -size 194850 +oid sha256:5703e4e80055b040382985fd7220c1a57ded04898745b49dfb3232f49ce240be +size 201719 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-en-GB.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-en-GB.Set-up.png index 91b57faa30..60a7d7c725 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-en-GB.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-en-GB.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:57ba61f6ee8a7d6a382851bfe161d922a8179a58d4ddd191a08eb9891ab8a30b -size 98103 +oid sha256:42f867e0fb0b0317b917b87c2bffbd857cd09857549da1246940f125a36b380e +size 102838 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-pseudo.Set-up.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-pseudo.Set-up.png index b8bc03f323..66d12ad1b7 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-pseudo.Set-up.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupKeyBackupScreen-iPhone-16-pseudo.Set-up.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea9ca53d25dd7f8fbd4453bca7dc02ffde0fd5bc43c0f183489080c5804fbc7c -size 153579 +oid sha256:d08e5521dcb2ebf681fd6a546e0804ad01e74b66063125686d7850d2df28b856 +size 160165 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Key-backup-disabled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Key-backup-disabled.png index 6b413c6e11..7778ec1bd1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Key-backup-disabled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Key-backup-disabled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:248c05bf396932033fdc1f2821659988b6cdde1558c4db1ff2e36c6eef9ae02d -size 109322 +oid sha256:3eefb4e846f077a2ca7b8297c505f780e7a604e7f9116b71cd1a9883113ccd0f +size 117078 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Key-backup-disabled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Key-backup-disabled.png index ae13453f5c..488cb1f4db 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Key-backup-disabled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Key-backup-disabled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:617075b51396eb2cbaddee74e2673f0b778b04f5f9263c8314dbdde774c6a233 -size 126840 +oid sha256:f687602f97c449269d2634bed2b9f81a85872b6f88364a96c52521729a222d3a +size 136021 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Key-backup-disabled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Key-backup-disabled.png index 8be7688aa1..1fc232f4cf 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Key-backup-disabled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Key-backup-disabled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1203fa1008ba94be887f96b286efa557a96577cf30ea0a3f882b9f7576b4c646 -size 66212 +oid sha256:5306c7452d991aea79d15d23f0b0c5363fd250d0eaf2c2e3a39d0b48b0f7fb7e +size 73582 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Key-backup-disabled.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Key-backup-disabled.png index 9f4565b683..2c5b8fa18d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Key-backup-disabled.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Key-backup-disabled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9199e5915f3292565b67005b242619cf670f07024c7097ab0e7af7bd5bc23710 -size 89974 +oid sha256:71b3bee5ed6add6cc6e4463be8b9099b47acc6d9c0b40dc26449efe2531d429e +size 103402 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_visualListItem-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_visualListItem-iPad-en-GB.1.png new file mode 100644 index 0000000000..eba4185de0 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_visualListItem-iPad-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9cac17036704df924eee98ae0287d231dceedca6ceee8ba990d4acb4d377f678 +size 110821 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_visualListItem-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_visualListItem-iPad-pseudo.1.png new file mode 100644 index 0000000000..811995eea4 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_visualListItem-iPad-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cd981618a53c74d5f3ee0c4bab370b4e431c760e94f1a97af742e2a82bc25f43 +size 118491 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_visualListItem-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_visualListItem-iPhone-16-en-GB.1.png new file mode 100644 index 0000000000..095ad6f626 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_visualListItem-iPhone-16-en-GB.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3734c22ad3f2a59633b28043fca8dee4038376e542b043ab05fc00fd93be364 +size 71605 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_visualListItem-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_visualListItem-iPhone-16-pseudo.1.png new file mode 100644 index 0000000000..2e24b8ba43 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_visualListItem-iPhone-16-pseudo.1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c688ca29c8ed4a490f20e081323f4ac300c9addf2a05662cd52d98f312f1b872 +size 85945 From 6f158264fa5aa5647137836d5eb6cf68b9f6cd1c Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Wed, 30 Oct 2024 16:36:51 +0000 Subject: [PATCH 095/114] Update verify identity button title. (#3466) --- ElementX/Resources/Localizations/en.lproj/Localizable.strings | 1 + ElementX/Sources/Generated/Strings.swift | 2 ++ .../RoomMemberDetailsScreenModels.swift | 4 ---- .../View/RoomMemberDetailsScreen.swift | 2 +- .../Screens/UserProfileScreen/UserProfileScreenModels.swift | 4 ---- .../Screens/UserProfileScreen/View/UserProfileScreen.swift | 2 +- .../test_roomMemberDetailsScreen-iPad-en-GB.Ignored-User.png | 4 ++-- .../test_roomMemberDetailsScreen-iPad-en-GB.Other-User.png | 4 ++-- .../test_roomMemberDetailsScreen-iPad-pseudo.Ignored-User.png | 4 ++-- .../test_roomMemberDetailsScreen-iPad-pseudo.Other-User.png | 4 ++-- ...t_roomMemberDetailsScreen-iPhone-16-en-GB.Ignored-User.png | 4 ++-- ...est_roomMemberDetailsScreen-iPhone-16-en-GB.Other-User.png | 4 ++-- ..._roomMemberDetailsScreen-iPhone-16-pseudo.Ignored-User.png | 4 ++-- ...st_roomMemberDetailsScreen-iPhone-16-pseudo.Other-User.png | 4 ++-- .../test_userProfileScreen-iPad-en-GB.Other-User.png | 4 ++-- .../test_userProfileScreen-iPad-pseudo.Other-User.png | 4 ++-- .../test_userProfileScreen-iPhone-16-en-GB.Other-User.png | 4 ++-- .../test_userProfileScreen-iPhone-16-pseudo.Other-User.png | 4 ++-- 18 files changed, 29 insertions(+), 34 deletions(-) diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 65ee807bcd..e5a5651e2e 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -234,6 +234,7 @@ "common_verification_failed" = "Verification failed"; "common_verified" = "Verified"; "common_verify_device" = "Verify device"; +"common_verify_identity" = "Verify identity"; "common_video" = "Video"; "common_voice_message" = "Voice message"; "common_waiting" = "Waiting…"; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 6f23eda763..573a347c04 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -526,6 +526,8 @@ internal enum L10n { internal static var commonVerified: String { return L10n.tr("Localizable", "common_verified") } /// Verify device internal static var commonVerifyDevice: String { return L10n.tr("Localizable", "common_verify_device") } + /// Verify identity + internal static var commonVerifyIdentity: String { return L10n.tr("Localizable", "common_verify_identity") } /// Video internal static var commonVideo: String { return L10n.tr("Localizable", "common_video") } /// Voice message diff --git a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenModels.swift b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenModels.swift index e55252e65e..ad319e9212 100644 --- a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenModels.swift +++ b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenModels.swift @@ -30,10 +30,6 @@ struct RoomMemberDetailsScreenViewState: BindableState { var showVerificationSection: Bool { isVerified == false && !isOwnMemberDetails } - - var verifyButtonTitle: String { - L10n.screenRoomMemberDetailsVerifyButtonTitle(memberDetails?.name ?? "") - } } struct RoomMemberDetailsScreenViewStateBindings { diff --git a/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift b/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift index 0e7be2d2f8..c075c5618f 100644 --- a/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift +++ b/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift @@ -86,7 +86,7 @@ struct RoomMemberDetailsScreen: View { var verificationSection: some View { if context.viewState.showVerificationSection { Section { - ListRow(label: .default(title: context.viewState.verifyButtonTitle, + ListRow(label: .default(title: L10n.commonVerifyIdentity, description: L10n.screenRoomMemberDetailsVerifyButtonSubtitle, icon: \.lock), kind: .button { }) diff --git a/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenModels.swift b/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenModels.swift index ba3ffd6e4e..3497c33192 100644 --- a/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenModels.swift +++ b/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenModels.swift @@ -32,10 +32,6 @@ struct UserProfileScreenViewState: BindableState { var showVerificationSection: Bool { isVerified == false && !isOwnUser } - - var verifyButtonTitle: String { - L10n.screenRoomMemberDetailsVerifyButtonTitle(userProfile?.displayName ?? "") - } } struct UserProfileScreenViewStateBindings { diff --git a/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift b/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift index 075b53a51c..7e90f0fdde 100644 --- a/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift +++ b/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift @@ -83,7 +83,7 @@ struct UserProfileScreen: View { var verificationSection: some View { if context.viewState.showVerificationSection { Section { - ListRow(label: .default(title: context.viewState.verifyButtonTitle, + ListRow(label: .default(title: L10n.commonVerifyIdentity, description: L10n.screenRoomMemberDetailsVerifyButtonSubtitle, icon: \.lock), kind: .button { }) diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Ignored-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Ignored-User.png index d89dcfc938..5e66f972ed 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Ignored-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Ignored-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4a9d05d2aea48c83dd1f0891eccbdc2d99a9b38176971efd097597e69fda28f -size 121678 +oid sha256:1ec63ab41577f807b574db65d04cad3e7274d7b684ed5c922103f2d21e79190e +size 121087 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Other-User.png index c820c177a8..861380ad25 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f2806abf81f7d716dd4a740bde06ca6e7d488db513f42bcb70ca4ef8a5c2505 -size 120435 +oid sha256:c6c4bb8c600440bfca120792553a0aba22f0d2da96a402b6451b9850d40a5f85 +size 120759 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Ignored-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Ignored-User.png index 1775940a9a..9c665866e9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Ignored-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Ignored-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b9d5ad23bdf44dbe689d44c03ad2a6f12e993bcf6d8245d614b3d9e3b004b8b9 -size 127141 +oid sha256:c12ffa3f032de0f340ad726938cab004203baf478830ed862862e2ff8566c435 +size 125206 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Other-User.png index e2b6c81cd0..3db539ebed 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1dcb000d6d4caae5edbfef5e9007d327b72332708a98fc1df54a35622f8eb220 -size 125657 +oid sha256:4585da982eba8c37919fba2f006bbe2e37b206704691b61bcaf06e3ed7cb47a7 +size 124706 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Ignored-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Ignored-User.png index a2845556dd..9529799c6d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Ignored-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Ignored-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:baa7127ec68c63490624fba7d6204063cdf950e941a55066291766e887c2281c -size 71929 +oid sha256:5fa4b9ddeef05139339a3e604e1567178cce02c3c03d19b309b2c9d8af441010 +size 71684 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Other-User.png index 858e2bfb03..498f55ea54 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de7639ceab51d7eb8306c7df3a9a6899c1e78a120e22358908f95f9791cb417f -size 70569 +oid sha256:dfde43627b92944423a6f1f2754fe5de95810aa2812576ec3225dcefb91e068e +size 70821 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Ignored-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Ignored-User.png index 5920bc5e6a..1b5f55b18d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Ignored-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Ignored-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d624776c5e6a14ddb5c21e7349a9e2b29499d45137c4dbe906f3ad1014d9c6d2 -size 84082 +oid sha256:7bae3f0db0d77faae018f19ace01ce3cde202db0fad1a2bc4618de37eba08aa8 +size 83579 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Other-User.png index d1c0729d73..957491ce31 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ddcf366a4fea427cd38681cff49842d7f5bc73ebc218efbe7ea9bc5914ae7b62 -size 82982 +oid sha256:4fb14af69f8a5d606ab5375d35d04ce5da7fa8af581b439f61a18fdb7b10e448 +size 82951 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Other-User.png index 4ca05e10c7..2f09b831a4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5995f233036158f3adaff3e86899e86ccace5c6b6991626c78e1514eb63c5d5 -size 115587 +oid sha256:94d690d16fdb9960e4da643bf30c63b9836ab15beac644e27ddd4bda28a01038 +size 114661 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Other-User.png index c750e0cb89..251c8f620d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:207bad6ef21783ebcc7c8189af09c233d191b01ddf7dc2fd92d3bbd1df1dd2ae -size 120261 +oid sha256:c4eec79fbfc398d28032b5911b48b04a0ece228eeb77f24068437a0cfb8cb9da +size 118148 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Other-User.png index 892b09ce16..475c0e1713 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5bd27ed946027cecc6286c1b4a0212b92addba260e7a17a650bb1f8a00ac0df9 -size 65717 +oid sha256:7979a1a28cc4bbe57e4b45db8278bcc8ddd2618b40985e22e473c9c49c3fef6d +size 65179 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Other-User.png index 3b7dc05d9c..6badb1172d 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a5dca5794cc839ed0d28f7868e044ef5aba075e5e44e0b3e43c9b70df7e642b8 -size 78100 +oid sha256:6b6ecdb7a7b7bcacf5c8aa3b0c083453fde5fa7ddd65c98e9d8ea33eb540cddc +size 77283 From 0da225205bce6f64a4f63a113e46c88d929a267c Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Thu, 31 Oct 2024 11:33:11 +0000 Subject: [PATCH 096/114] Update the strings for out of sync Key Storage. (#3468) * Update the banner strings for out of sync key storage. * Update string on SecureBackupScreen. * Update snapshots * Update an alert string we weren't using on iOS, and use it at the same place as Android. --- .../en.lproj/Localizable.strings | 18 ++++--- ElementX/Sources/Generated/Strings.swift | 10 ++-- ...eScreenRecoveryKeyConfirmationBanner.swift | 50 ++++++++++++------- ...cureBackupRecoveryKeyScreenViewModel.swift | 4 +- ...firmationBanner-iPad-en-GB.Out-of-sync.png | 4 +- ...ationBanner-iPad-en-GB.Set-up-recovery.png | 2 +- ...irmationBanner-iPad-pseudo.Out-of-sync.png | 4 +- ...tionBanner-iPad-pseudo.Set-up-recovery.png | 4 +- ...tionBanner-iPhone-16-en-GB.Out-of-sync.png | 4 +- ...Banner-iPhone-16-en-GB.Set-up-recovery.png | 4 +- ...ionBanner-iPhone-16-pseudo.Out-of-sync.png | 4 +- ...anner-iPhone-16-pseudo.Set-up-recovery.png | 4 +- ...pScreen-iPad-en-GB.Recovery-incomplete.png | 4 +- ...Screen-iPad-pseudo.Recovery-incomplete.png | 4 +- ...en-iPhone-16-en-GB.Recovery-incomplete.png | 4 +- ...n-iPhone-16-pseudo.Recovery-incomplete.png | 4 +- 16 files changed, 76 insertions(+), 52 deletions(-) diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index e5a5651e2e..8a024b3a44 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -116,7 +116,6 @@ "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; "banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; "banner_set_up_recovery_content" = "Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices."; -"banner_set_up_recovery_submit" = "Set up recovery"; "banner_set_up_recovery_title" = "Set up recovery to protect your account"; "common_about" = "About"; "common_acceptable_use_policy" = "Acceptable use policy"; @@ -247,8 +246,10 @@ "common.you" = "You"; "common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; "common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; -"confirm_recovery_key_banner_message" = "Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup."; -"confirm_recovery_key_banner_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_message" = "Confirm your recovery key to maintain access to your key storage and message history."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; +"confirm_recovery_key_banner_title" = "Your key storage is out of sync"; "crash_detection_dialog_content" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; @@ -465,8 +466,7 @@ "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_confirm_description" = "Your key storage is currently out of sync."; "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."; "screen_create_account_title" = "Create account"; "screen_create_new_recovery_key_list_item_1" = "Open %1$@ in a desktop device"; @@ -641,7 +641,7 @@ "screen_recovery_key_change_title" = "Change recovery key?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Create new recovery key"; "screen_recovery_key_confirm_description" = "Make sure nobody can see this screen!"; -"screen_recovery_key_confirm_error_content" = "Please try again to confirm access to your chat backup."; +"screen_recovery_key_confirm_error_content" = "Please try again to confirm access to your key storage."; "screen_recovery_key_confirm_error_title" = "Incorrect recovery key"; "screen_recovery_key_confirm_key_description" = "If you have a security key or security phrase, this will work too."; "screen_recovery_key_confirm_key_placeholder" = "Enter…"; @@ -789,7 +789,6 @@ "screen_room_timeline_less_reactions" = "Show less"; "screen_room_timeline_message_copied" = "Message copied"; "screen_room_timeline_no_permission_to_post" = "You do not have permission to post to this room"; -"screen_room_timeline_reactions_show_less" = "Show less"; "screen_room_timeline_reactions_show_more" = "Show more"; "screen_room_timeline_read_marker_title" = "New"; "screen_room_title" = "Chat"; @@ -839,7 +838,6 @@ "screen_session_verification_ready_subtitle" = "Compare a unique set of emojis."; "screen_session_verification_request_accepted_subtitle" = "Compare the unique emoji, ensuring they appear in the same order."; "screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; @@ -995,6 +993,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "No push distributors found."; "troubleshoot_notifications_test_unified_push_title" = "Check UnifiedPush"; "a11y_poll" = "Poll"; +"banner_set_up_recovery_submit" = "Set up recovery"; "dialog_title_error" = "Error"; "dialog_title_success" = "Success"; "notification_fallback_content" = "Notification"; @@ -1015,6 +1014,7 @@ "screen_blocked_users_unblock_alert_title" = "Unblock user"; "screen_bug_report_rash_logs_alert_title" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; "screen_chat_backup_recovery_action_confirm" = "Enter recovery key"; +"screen_chat_backup_recovery_action_setup" = "Set up recovery"; "screen_create_poll_cancel_confirmation_content_ios" = "Your changes won’t be saved"; "screen_create_room_add_people_title" = "Invite people"; "screen_create_room_room_name_label" = "Room name"; @@ -1055,8 +1055,10 @@ "screen_room_error_failed_processing_media" = "Failed processing media to upload, please try again."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Remove and ban member"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Mentions and Keywords only"; +"screen_room_timeline_reactions_show_less" = "Show less"; "screen_roomlist_filter_people" = "People"; "screen_server_confirmation_change_server" = "Change account provider"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "Sign out"; "screen_signout_confirmation_dialog_title" = "Sign out"; "screen_signout_key_backup_offline_title" = "Your keys are still being backed up"; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 573a347c04..e048a17eb4 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -536,9 +536,13 @@ internal enum L10n { internal static var commonWaiting: String { return L10n.tr("Localizable", "common_waiting") } /// Waiting for this message internal static var commonWaitingForDecryptionKey: String { return L10n.tr("Localizable", "common_waiting_for_decryption_key") } - /// Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup. + /// Confirm your recovery key to maintain access to your key storage and message history. internal static var confirmRecoveryKeyBannerMessage: String { return L10n.tr("Localizable", "confirm_recovery_key_banner_message") } /// Enter your recovery key + internal static var confirmRecoveryKeyBannerPrimaryButtonTitle: String { return L10n.tr("Localizable", "confirm_recovery_key_banner_primary_button_title") } + /// Forgot your recovery key? + internal static var confirmRecoveryKeyBannerSecondaryButtonTitle: String { return L10n.tr("Localizable", "confirm_recovery_key_banner_secondary_button_title") } + /// Your key storage is out of sync internal static var confirmRecoveryKeyBannerTitle: String { return L10n.tr("Localizable", "confirm_recovery_key_banner_title") } /// %1$@ crashed the last time it was used. Would you like to share a crash report with us? internal static func crashDetectionDialogContent(_ p1: Any) -> String { @@ -1039,7 +1043,7 @@ internal enum L10n { 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. + /// Your key storage is currently out of sync. internal static var screenChatBackupRecoveryActionConfirmDescription: String { return L10n.tr("Localizable", "screen_chat_backup_recovery_action_confirm_description") } /// Set up recovery internal static var screenChatBackupRecoveryActionSetup: String { return L10n.tr("Localizable", "screen_chat_backup_recovery_action_setup") } @@ -1516,7 +1520,7 @@ internal enum L10n { internal static var screenRecoveryKeyConfirmCreateNewRecoveryKey: String { return L10n.tr("Localizable", "screen_recovery_key_confirm_create_new_recovery_key") } /// Make sure nobody can see this screen! internal static var screenRecoveryKeyConfirmDescription: String { return L10n.tr("Localizable", "screen_recovery_key_confirm_description") } - /// Please try again to confirm access to your chat backup. + /// Please try again to confirm access to your key storage. internal static var screenRecoveryKeyConfirmErrorContent: String { return L10n.tr("Localizable", "screen_recovery_key_confirm_error_content") } /// Incorrect recovery key internal static var screenRecoveryKeyConfirmErrorTitle: String { return L10n.tr("Localizable", "screen_recovery_key_confirm_error_title") } diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift index e801e7d218..3215723301 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift @@ -6,6 +6,7 @@ // import Combine +import Compound import SwiftUI struct HomeScreenRecoveryKeyConfirmationBanner: View { @@ -14,18 +15,28 @@ struct HomeScreenRecoveryKeyConfirmationBanner: View { var title: String { requiresExtraAccountSetup ? L10n.bannerSetUpRecoveryTitle : L10n.confirmRecoveryKeyBannerTitle } var message: String { requiresExtraAccountSetup ? L10n.bannerSetUpRecoveryContent : L10n.confirmRecoveryKeyBannerMessage } - var actionTitle: String { requiresExtraAccountSetup ? L10n.bannerSetUpRecoverySubmit : L10n.actionContinue } + var actionTitle: String { requiresExtraAccountSetup ? L10n.bannerSetUpRecoverySubmit : L10n.confirmRecoveryKeyBannerPrimaryButtonTitle } var body: some View { - VStack(alignment: .leading, spacing: 16) { - VStack(alignment: .leading, spacing: 4) { - HStack(spacing: 16) { - Text(title) - .font(.compound.bodyLGSemibold) - .foregroundColor(.compound.textPrimary) - - Spacer() - + VStack(spacing: 16) { + content + buttons + } + .padding(16) + .background(Color.compound.bgSubtleSecondary) + .cornerRadius(14) + .padding(.horizontal, 16) + } + + var content: some View { + VStack(alignment: .leading, spacing: 4) { + HStack(alignment: .firstTextBaseline, spacing: 16) { + Text(title) + .font(.compound.bodyLGSemibold) + .foregroundColor(.compound.textPrimary) + .frame(maxWidth: .infinity, alignment: .leading) + + if requiresExtraAccountSetup { Button { context.send(viewAction: .skipRecoveryKeyConfirmation) } label: { @@ -34,22 +45,27 @@ struct HomeScreenRecoveryKeyConfirmationBanner: View { .frame(width: 12, height: 12) } } - Text(message) - .font(.compound.bodyMD) - .foregroundColor(.compound.textSecondary) } + Text(message) + .font(.compound.bodyMD) + .foregroundColor(.compound.textSecondary) + } + } + + var buttons: some View { + VStack(spacing: 16) { Button(actionTitle) { context.send(viewAction: .confirmRecoveryKey) } .frame(maxWidth: .infinity) .buttonStyle(.compound(.primary, size: .medium)) .accessibilityIdentifier(A11yIdentifiers.homeScreen.recoveryKeyConfirmationBannerContinue) + + if !requiresExtraAccountSetup { + // Missing encryption reset button to goes here once the flow exists. + } } - .padding(16) - .background(Color.compound.bgSubtleSecondary) - .cornerRadius(14) - .padding(.horizontal, 16) } } diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift index 3b03ac84c6..84c31fb8c7 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift @@ -65,7 +65,9 @@ class SecureBackupRecoveryKeyScreenViewModel: SecureBackupRecoveryKeyScreenViewM actionsSubject.send(.done(mode: context.viewState.mode)) case .failure(let error): MXLog.error("Failed confirming recovery key with error: \(error)") - state.bindings.alertInfo = .init(id: .init()) + state.bindings.alertInfo = .init(id: .init(), + title: L10n.screenRecoveryKeyConfirmErrorTitle, + message: L10n.screenRecoveryKeyConfirmErrorContent) } hideLoadingIndicator() diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Out-of-sync.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Out-of-sync.png index 04511fd48f..d9e29da1ad 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Out-of-sync.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Out-of-sync.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4755216723bde1c66554701adf2fefec3f6a4b2949d66ac5875a801b1cd3d8f -size 95958 +oid sha256:d425040e4de7d03d440c14ad4c1255e9ecd0950e913e340e6828780b008dddbb +size 98068 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Set-up-recovery.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Set-up-recovery.png index 217f9b59bf..09de4f3783 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Set-up-recovery.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Set-up-recovery.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e84c622fbb683ac69fa5eb646de5bd340c6105cb08dddd7ab5ecae9d55299583 +oid sha256:580afc74fbb53628ec66643e67c643fab23ea1e2e0717de0f8622534b0cf1724 size 101351 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Out-of-sync.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Out-of-sync.png index 394ef0f5f0..6ae3571a04 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Out-of-sync.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Out-of-sync.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ea3d780c157fc77da9a75f73d8b482f7d23eb539a67e854dba0ba44deda6173 -size 110663 +oid sha256:946bbda26c484f64738933c2c4fd3f4b8d095079d38947ef55c17deb4660b1a6 +size 109879 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Set-up-recovery.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Set-up-recovery.png index 9b05eb497d..88b3693a97 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Set-up-recovery.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Set-up-recovery.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:934180921dd8b9c7feeb270fcf3a6d1a2847edba5fc4ea4c820a95b60080c218 -size 119120 +oid sha256:b1c9b76234430e9d6d446ffd28220b1f6a370fc2239885fc55fff282e413b3d2 +size 119143 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Out-of-sync.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Out-of-sync.png index 9128476ef8..a0b3aa0e0b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Out-of-sync.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Out-of-sync.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c788a03807032eac004c08138a80590a7cecf1c6a4d229a612fbfd9a2f8cf489 -size 58527 +oid sha256:32b70c1911a7a413e0d6ae1748e3ea05929e951503629ead0b2fe3773f0bdd9d +size 56470 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Set-up-recovery.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Set-up-recovery.png index b21ba8455e..bb613b8d1b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Set-up-recovery.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Set-up-recovery.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aec6cef7b010741b2db19085da28d43c3a1a511a658fa957236d665d5aaf6429 -size 65400 +oid sha256:adf6a9788518c6b10fb2c7369358b86004fba197f97913f2b74fade671e62046 +size 65418 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Out-of-sync.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Out-of-sync.png index 9c5c1a5eb9..4d2e4c92c1 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Out-of-sync.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Out-of-sync.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7284ecd8fd558b1ae9a746d27f690ef931bfccf4fb1308c2d7778ba3795a158 -size 79847 +oid sha256:e9e6a4ef76a75e88c0a87e177fd11ec6d1bb9b1b6f843692d002f93b4fa75690 +size 79643 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Set-up-recovery.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Set-up-recovery.png index 9b06387962..0a0053f5af 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Set-up-recovery.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Set-up-recovery.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:338ad333cedcb48ce8ea5ba3573956f122e9a48fd411249f803b32c8b755a75b -size 92631 +oid sha256:894d0d1de52a98c6446e955ce68c1d7c2f0d14496742c357410653c8a1c12a61 +size 92453 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Recovery-incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Recovery-incomplete.png index 8c01901979..dc360e4a96 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Recovery-incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-en-GB.Recovery-incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff079b6cd61f2c0fb2c4a7a66dd1cdbc297b9ee0951fad8274b0d279507544ca -size 91489 +oid sha256:1074a4fc754d8adea55068deb0371423986258a6a33052983360a71d26a08c77 +size 91504 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Recovery-incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Recovery-incomplete.png index 05a8132d7e..b9e4afbbdc 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Recovery-incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPad-pseudo.Recovery-incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:47fe19f5a821b1ef13956f05ea55ec4f9d22ec8846a5596b318eec1cc0974071 -size 94488 +oid sha256:e9e29742d2bd0b51b691f90bcda2b3575804ce4bdab3cdecdfc5b80572edba53 +size 96179 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Recovery-incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Recovery-incomplete.png index 66dcae71d1..72dd634c21 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Recovery-incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-en-GB.Recovery-incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb3b0d4c592e7dd54650f94bcdbf1d3763dd946c7f8c753b024a27d24e3d7cdb -size 45210 +oid sha256:9634914eb74dce6716f80606d010fc3f32a25f84f8734c42db5944dcbd41e4d4 +size 44940 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Recovery-incomplete.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Recovery-incomplete.png index c72a721dd3..ffe2b68558 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Recovery-incomplete.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_secureBackupScreen-iPhone-16-pseudo.Recovery-incomplete.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0709b19f855c68b6c72d16a6377ef96a388ffec5604c2d66492c8602cfa11e90 -size 53200 +oid sha256:0aa9d066e4646d6f0dc322d02514343fc9677a5ff33b71c1c4171063ccb98a1a +size 52909 From 1f90f1a9f1e4aa366ae41d92c8880f9959daade0 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:14:14 +0000 Subject: [PATCH 097/114] Enable the Optimised Media Uploads feature. (#3467) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Enable the Optimised Media Uploads feature. (Well move the toggle from Developer Options to Advanced Settings) * Add OptimizeMediaUploads analytics. * Final strings. * Upload reduced quality media by default 😢 Move the setting out of the feature flags section in the file. * Fix unit tests now the default has changed. * Pull in updated string, fix snapshots. --- ElementX.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 ++-- .../Localizations/en.lproj/Localizable.strings | 4 ++-- ElementX/Sources/Application/AppSettings.swift | 8 ++++---- .../SettingsFlowCoordinator.swift | 6 ++++-- .../UserSessionFlowCoordinator.swift | 3 ++- ElementX/Sources/Generated/Strings.swift | 4 ++-- .../AdvancedSettingsScreenCoordinator.swift | 9 +++++++-- .../AdvancedSettingsScreenModels.swift | 6 +++++- .../AdvancedSettingsScreenViewModel.swift | 16 ++++++++++++---- .../View/AdvancedSettingsScreen.swift | 13 +++++++++++-- .../DeveloperOptionsScreenModels.swift | 1 - .../View/DeveloperOptionsScreen.swift | 6 ------ .../test_advancedSettingsScreen-iPad-en-GB.1.png | 4 ++-- ...test_advancedSettingsScreen-iPad-pseudo.1.png | 4 ++-- ..._advancedSettingsScreen-iPhone-16-en-GB.1.png | 4 ++-- ...advancedSettingsScreen-iPhone-16-pseudo.1.png | 4 ++-- .../MediaUploadingPreprocessorTests.swift | 1 + project.yml | 2 +- 19 files changed, 62 insertions(+), 39 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 88da35f4a1..a0c529c6ef 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7874,7 +7874,7 @@ repositoryURL = "https://github.com/matrix-org/matrix-analytics-events"; requirement = { kind = upToNextMinorVersion; - minimumVersion = 0.27.0; + minimumVersion = 0.28.0; }; }; C13F55E4518415CB4C278E73 /* XCRemoteSwiftPackageReference "DTCoreText" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 97e4b2e1c1..4751bc7db7 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -131,8 +131,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/matrix-org/matrix-analytics-events", "state" : { - "revision" : "9bd3c57e84f87d56b69862369f3b9da714d1d151", - "version" : "0.27.0" + "revision" : "632f4266d5ebd5b87b9eb52522f5117723fcd338", + "version" : "0.28.0" } }, { diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 8a024b3a44..02dbfdfa63 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -391,8 +391,8 @@ "screen_account_provider_signup_title" = "You’re about to create an account on %@"; "screen_advanced_settings_developer_mode" = "Developer mode"; "screen_advanced_settings_developer_mode_description" = "Enable to have access to features and functionality for developers."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "Disable the rich text editor to type Markdown manually."; "screen_advanced_settings_send_read_receipts" = "Read receipts"; "screen_advanced_settings_send_read_receipts_description" = "If turned off, your read receipts won't be sent to anyone. You will still receive read receipts from other users."; diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index c759358e23..40b0d76bfe 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -32,6 +32,7 @@ final class AppSettings { case pusherProfileTag case logLevel case viewSourceEnabled + case optimizeMediaUploads case appAppearance case sharePresence case hideUnreadMessagesBadge @@ -42,7 +43,6 @@ final class AppSettings { // Feature flags case slidingSyncDiscovery - case optimizeMediaUploads case publicSearchEnabled case fuzzyRoomListSearchEnabled case enableOnlySignedDeviceIsolationMode @@ -237,6 +237,9 @@ final class AppSettings { @UserPreference(key: UserDefaultsKeys.viewSourceEnabled, defaultValue: isDevelopmentBuild, storageType: .userDefaults(store)) var viewSourceEnabled + + @UserPreference(key: UserDefaultsKeys.optimizeMediaUploads, defaultValue: true, storageType: .userDefaults(store)) + var optimizeMediaUploads // MARK: - Element Call @@ -275,9 +278,6 @@ final class AppSettings { @UserPreference(key: UserDefaultsKeys.slidingSyncDiscovery, defaultValue: .native, storageType: .userDefaults(store)) var slidingSyncDiscovery: SlidingSyncDiscovery - @UserPreference(key: UserDefaultsKeys.optimizeMediaUploads, defaultValue: false, storageType: .userDefaults(store)) - var optimizeMediaUploads - @UserPreference(key: UserDefaultsKeys.knockingEnabled, defaultValue: false, storageType: .userDefaults(store)) var knockingEnabled diff --git a/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift index 7e031a0005..3ae3d31cfa 100644 --- a/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift @@ -27,6 +27,7 @@ struct SettingsFlowCoordinatorParameters { let appSettings: AppSettings let navigationSplitCoordinator: NavigationSplitCoordinator let userIndicatorController: UserIndicatorControllerProtocol + let analytics: AnalyticsService } class SettingsFlowCoordinator: FlowCoordinatorProtocol { @@ -174,7 +175,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol { private func presentAnalyticsScreen() { let coordinator = AnalyticsSettingsScreenCoordinator(parameters: .init(appSettings: parameters.appSettings, - analytics: ServiceLocator.shared.analytics)) + analytics: parameters.analytics)) navigationStackCoordinator?.push(coordinator) } @@ -221,7 +222,8 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol { } private func presentAdvancedSettings() { - let coordinator = AdvancedSettingsScreenCoordinator() + let coordinator = AdvancedSettingsScreenCoordinator(parameters: .init(appSettings: parameters.appSettings, + analytics: parameters.analytics)) navigationStackCoordinator.push(coordinator) } diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift index 012c26dfb9..8e2431e541 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift @@ -99,7 +99,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { secureBackupController: userSession.clientProxy.secureBackupController, appSettings: appSettings, navigationSplitCoordinator: navigationSplitCoordinator, - userIndicatorController: ServiceLocator.shared.userIndicatorController)) + userIndicatorController: ServiceLocator.shared.userIndicatorController, + analytics: analytics)) onboardingFlowCoordinator = OnboardingFlowCoordinator(userSession: userSession, appLockService: appLockService, diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index e048a17eb4..6f2f69e0bb 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -846,9 +846,9 @@ internal enum L10n { internal static var screenAdvancedSettingsElementCallBaseUrlDescription: String { return L10n.tr("Localizable", "screen_advanced_settings_element_call_base_url_description") } /// Invalid URL, please make sure you include the protocol (http/https) and the correct address. internal static var screenAdvancedSettingsElementCallBaseUrlValidationError: String { return L10n.tr("Localizable", "screen_advanced_settings_element_call_base_url_validation_error") } - /// Optimize for upload + /// Upload photos and videos faster and reduce data usage internal static var screenAdvancedSettingsMediaCompressionDescription: String { return L10n.tr("Localizable", "screen_advanced_settings_media_compression_description") } - /// Media + /// Optimise media quality internal static var screenAdvancedSettingsMediaCompressionTitle: String { return L10n.tr("Localizable", "screen_advanced_settings_media_compression_title") } /// Disable the rich text editor to type Markdown manually. internal static var screenAdvancedSettingsRichTextEditorDescription: String { return L10n.tr("Localizable", "screen_advanced_settings_rich_text_editor_description") } diff --git a/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenCoordinator.swift b/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenCoordinator.swift index 20715a393f..1094a14a12 100644 --- a/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenCoordinator.swift @@ -8,11 +8,16 @@ import Combine import SwiftUI +struct AdvancedSettingsScreenCoordinatorParameters { + let appSettings: AppSettings + let analytics: AnalyticsService +} + final class AdvancedSettingsScreenCoordinator: CoordinatorProtocol { private var viewModel: AdvancedSettingsScreenViewModelProtocol - init() { - viewModel = AdvancedSettingsScreenViewModel(advancedSettings: ServiceLocator.shared.settings) + init(parameters: AdvancedSettingsScreenCoordinatorParameters) { + viewModel = AdvancedSettingsScreenViewModel(advancedSettings: parameters.appSettings, analytics: parameters.analytics) } func toPresentable() -> AnyView { diff --git a/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenModels.swift b/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenModels.swift index 35f0e8148e..2003ae6688 100644 --- a/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenModels.swift @@ -26,12 +26,16 @@ struct AdvancedSettingsScreenViewStateBindings { } } -enum AdvancedSettingsScreenViewAction { } +enum AdvancedSettingsScreenViewAction { + case optimizeMediaUploadsChanged +} protocol AdvancedSettingsProtocol: AnyObject { var viewSourceEnabled: Bool { get set } var appAppearance: AppAppearance { get set } var sharePresence: Bool { get set } + + var optimizeMediaUploads: Bool { get set } } extension AppSettings: AdvancedSettingsProtocol { } diff --git a/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenViewModel.swift b/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenViewModel.swift index cc7e51a561..a7a05932a1 100644 --- a/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenViewModel.swift +++ b/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenViewModel.swift @@ -11,12 +11,20 @@ import SwiftUI typealias AdvancedSettingsScreenViewModelType = StateStoreViewModel class AdvancedSettingsScreenViewModel: AdvancedSettingsScreenViewModelType, AdvancedSettingsScreenViewModelProtocol { - init(advancedSettings: AdvancedSettingsProtocol) { - let bindings = AdvancedSettingsScreenViewStateBindings(advancedSettings: advancedSettings) - let state = AdvancedSettingsScreenViewState(bindings: bindings) + private let analytics: AnalyticsService + + init(advancedSettings: AdvancedSettingsProtocol, analytics: AnalyticsService) { + self.analytics = analytics + let state = AdvancedSettingsScreenViewState(bindings: .init(advancedSettings: advancedSettings)) super.init(initialViewState: state) } - override func process(viewAction: AdvancedSettingsScreenViewAction) { } + override func process(viewAction: AdvancedSettingsScreenViewAction) { + switch viewAction { + case .optimizeMediaUploadsChanged: + // Note: Using a view action here as sinking the AppSettings publisher tracks the initial value. + analytics.trackInteraction(name: state.bindings.optimizeMediaUploads ? .MobileSettingsOptimizeMediaUploadsEnabled : .MobileSettingsOptimizeMediaUploadsDisabled) + } + } } diff --git a/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/View/AdvancedSettingsScreen.swift b/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/View/AdvancedSettingsScreen.swift index 17436c310a..1c2e9fb29e 100644 --- a/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/View/AdvancedSettingsScreen.swift +++ b/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/View/AdvancedSettingsScreen.swift @@ -18,12 +18,20 @@ struct AdvancedSettingsScreen: View { kind: .picker(selection: $context.appAppearance, items: AppAppearance.allCases.map { (title: $0.name, tag: $0) })) - ListRow(label: .plain(title: L10n.actionViewSource), + ListRow(label: .plain(title: L10n.actionViewSource, + description: L10n.screenAdvancedSettingsViewSourceDescription), kind: .toggle($context.viewSourceEnabled)) ListRow(label: .plain(title: L10n.screenAdvancedSettingsSharePresence, description: L10n.screenAdvancedSettingsSharePresenceDescription), kind: .toggle($context.sharePresence)) + + ListRow(label: .plain(title: L10n.screenAdvancedSettingsMediaCompressionTitle, + description: L10n.screenAdvancedSettingsMediaCompressionDescription), + kind: .toggle($context.optimizeMediaUploads)) + .onChange(of: context.optimizeMediaUploads) { + context.send(viewAction: .optimizeMediaUploadsChanged) + } } } .compoundList() @@ -48,7 +56,8 @@ private extension AppAppearance { // MARK: - Previews struct AdvancedSettingsScreen_Previews: PreviewProvider, TestablePreview { - static let viewModel = AdvancedSettingsScreenViewModel(advancedSettings: ServiceLocator.shared.settings) + static let viewModel = AdvancedSettingsScreenViewModel(advancedSettings: ServiceLocator.shared.settings, + analytics: ServiceLocator.shared.analytics) static var previews: some View { NavigationStack { AdvancedSettingsScreen(context: viewModel.context) diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift index 62e4d7f9b0..f8523ff3c1 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift @@ -46,7 +46,6 @@ protocol DeveloperOptionsProtocol: AnyObject { var hideUnreadMessagesBadge: Bool { get set } var fuzzyRoomListSearchEnabled: Bool { get set } var hideTimelineMedia: Bool { get set } - var optimizeMediaUploads: Bool { get set } var enableOnlySignedDeviceIsolationMode: Bool { get set } var elementCallBaseURLOverride: URL? { get set } var knockingEnabled: Bool { get set } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift index bf4ed095aa..d9e6f2a6cd 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -62,12 +62,6 @@ struct DeveloperOptionsScreen: View { } } - Section("Media") { - Toggle(isOn: $context.optimizeMediaUploads) { - Text("Optimise for upload") - } - } - Section { Toggle(isOn: $context.enableOnlySignedDeviceIsolationMode) { Text("Exclude insecure devices when sending/receiving messages") diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_advancedSettingsScreen-iPad-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_advancedSettingsScreen-iPad-en-GB.1.png index c6d1158f0a..3f41508339 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_advancedSettingsScreen-iPad-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_advancedSettingsScreen-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:484136606f2b3dd8cdc29f5b4f943622c2e770672c5a1980ff0e7db55a499c09 -size 114860 +oid sha256:7dbc230120926d73fde55e42c43e36ef868bd893e600e749c66f979daa3f5659 +size 139055 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_advancedSettingsScreen-iPad-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_advancedSettingsScreen-iPad-pseudo.1.png index 2ffab2fc07..ee91d09f82 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_advancedSettingsScreen-iPad-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_advancedSettingsScreen-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bdfd7ebbf5f14f045bdaf416aa405d277edce74fdd3f7040f82b488e60074dc1 -size 126470 +oid sha256:e71fa4df1e53f4fde23993f2242cc6c1cf239a78bf4a9b46aa1f982f32ae680d +size 159915 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_advancedSettingsScreen-iPhone-16-en-GB.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_advancedSettingsScreen-iPhone-16-en-GB.1.png index 2dfdb18eb5..169636eac9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_advancedSettingsScreen-iPhone-16-en-GB.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_advancedSettingsScreen-iPhone-16-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:987c192f5c1ab4c07fc198c190b3867843d9b459e6edf71a5057102853fe9941 -size 66997 +oid sha256:a505289fc465bfdb3a40660c5fff6828c528574669cd4967fd9c63ce810043f8 +size 89637 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_advancedSettingsScreen-iPhone-16-pseudo.1.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_advancedSettingsScreen-iPhone-16-pseudo.1.png index e1f4810213..e46e2828f5 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_advancedSettingsScreen-iPhone-16-pseudo.1.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_advancedSettingsScreen-iPhone-16-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd509058af6af0c07b4350bf19e33dc16ca76c977be0c99d03614f4810374235 -size 84485 +oid sha256:94b8e86129726c0f05a02136b7afa70cb0ae080896d4bc67841dbfa81e7fc3ac +size 127155 diff --git a/UnitTests/Sources/MediaUploadingPreprocessorTests.swift b/UnitTests/Sources/MediaUploadingPreprocessorTests.swift index 68734fada8..93ae732697 100644 --- a/UnitTests/Sources/MediaUploadingPreprocessorTests.swift +++ b/UnitTests/Sources/MediaUploadingPreprocessorTests.swift @@ -17,6 +17,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase { override func setUp() { AppSettings.resetAllSettings() appSettings = AppSettings() + appSettings.optimizeMediaUploads = false ServiceLocator.shared.register(appSettings: appSettings) mediaUploadingPreprocessor = MediaUploadingPreprocessor(appSettings: appSettings) } diff --git a/project.yml b/project.yml index 3e2e9dac54..aeae881b60 100644 --- a/project.yml +++ b/project.yml @@ -68,7 +68,7 @@ packages: # path: ../compound-ios AnalyticsEvents: url: https://github.com/matrix-org/matrix-analytics-events - minorVersion: 0.27.0 + minorVersion: 0.28.0 # path: ../matrix-analytics-events Emojibase: url: https://github.com/matrix-org/emojibase-bindings From c34e3e8d943eae85911abd06d97a0bbc156da49d Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 4 Nov 2024 11:40:19 +0100 Subject: [PATCH 098/114] update sdk --- ElementX.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- .../Mocks/Generated/SDKGeneratedMocks.swift | 296 ++++++++++++++++-- .../RoomDirectorySearchProxy.swift | 2 +- project.yml | 2 +- 5 files changed, 283 insertions(+), 23 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index a0c529c6ef..bab5fb81f7 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7834,7 +7834,7 @@ repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.0.62; + version = 1.0.63; }; }; 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 4751bc7db7..eff2b29b05 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/matrix-rust-components-swift", "state" : { - "revision" : "9b26e40ae6c27c56e233577c863569ff074f84fd", - "version" : "1.0.62" + "revision" : "0dcb3a3ee8b0b1bb92c2e9d6395202e2547fe0d8", + "version" : "1.0.63" } }, { diff --git a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift index ec51a42ede..94918f2205 100644 --- a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift @@ -7047,6 +7047,71 @@ open class HomeserverLoginDetailsSDKMock: MatrixRustSDK.HomeserverLoginDetails { } } + //MARK: - supportedOidcPrompts + + var supportedOidcPromptsUnderlyingCallsCount = 0 + open var supportedOidcPromptsCallsCount: Int { + get { + if Thread.isMainThread { + return supportedOidcPromptsUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = supportedOidcPromptsUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + supportedOidcPromptsUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + supportedOidcPromptsUnderlyingCallsCount = newValue + } + } + } + } + open var supportedOidcPromptsCalled: Bool { + return supportedOidcPromptsCallsCount > 0 + } + + var supportedOidcPromptsUnderlyingReturnValue: [OidcPrompt]! + open var supportedOidcPromptsReturnValue: [OidcPrompt]! { + get { + if Thread.isMainThread { + return supportedOidcPromptsUnderlyingReturnValue + } else { + var returnValue: [OidcPrompt]? = nil + DispatchQueue.main.sync { + returnValue = supportedOidcPromptsUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + supportedOidcPromptsUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + supportedOidcPromptsUnderlyingReturnValue = newValue + } + } + } + } + open var supportedOidcPromptsClosure: (() -> [OidcPrompt])? + + open override func supportedOidcPrompts() -> [OidcPrompt] { + supportedOidcPromptsCallsCount += 1 + if let supportedOidcPromptsClosure = supportedOidcPromptsClosure { + return supportedOidcPromptsClosure() + } else { + return supportedOidcPromptsReturnValue + } + } + //MARK: - supportsOidcLogin var supportsOidcLoginUnderlyingCallsCount = 0 @@ -14359,16 +14424,16 @@ open class RoomDirectorySearchSDKMock: MatrixRustSDK.RoomDirectorySearch { //MARK: - search - open var searchFilterBatchSizeThrowableError: Error? - var searchFilterBatchSizeUnderlyingCallsCount = 0 - open var searchFilterBatchSizeCallsCount: Int { + open var searchFilterBatchSizeViaServerNameThrowableError: Error? + var searchFilterBatchSizeViaServerNameUnderlyingCallsCount = 0 + open var searchFilterBatchSizeViaServerNameCallsCount: Int { get { if Thread.isMainThread { - return searchFilterBatchSizeUnderlyingCallsCount + return searchFilterBatchSizeViaServerNameUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = searchFilterBatchSizeUnderlyingCallsCount + returnValue = searchFilterBatchSizeViaServerNameUnderlyingCallsCount } return returnValue! @@ -14376,31 +14441,31 @@ open class RoomDirectorySearchSDKMock: MatrixRustSDK.RoomDirectorySearch { } set { if Thread.isMainThread { - searchFilterBatchSizeUnderlyingCallsCount = newValue + searchFilterBatchSizeViaServerNameUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - searchFilterBatchSizeUnderlyingCallsCount = newValue + searchFilterBatchSizeViaServerNameUnderlyingCallsCount = newValue } } } } - open var searchFilterBatchSizeCalled: Bool { - return searchFilterBatchSizeCallsCount > 0 + open var searchFilterBatchSizeViaServerNameCalled: Bool { + return searchFilterBatchSizeViaServerNameCallsCount > 0 } - open var searchFilterBatchSizeReceivedArguments: (filter: String?, batchSize: UInt32)? - open var searchFilterBatchSizeReceivedInvocations: [(filter: String?, batchSize: UInt32)] = [] - open var searchFilterBatchSizeClosure: ((String?, UInt32) async throws -> Void)? + open var searchFilterBatchSizeViaServerNameReceivedArguments: (filter: String?, batchSize: UInt32, viaServerName: String?)? + open var searchFilterBatchSizeViaServerNameReceivedInvocations: [(filter: String?, batchSize: UInt32, viaServerName: String?)] = [] + open var searchFilterBatchSizeViaServerNameClosure: ((String?, UInt32, String?) async throws -> Void)? - open override func search(filter: String?, batchSize: UInt32) async throws { - if let error = searchFilterBatchSizeThrowableError { + open override func search(filter: String?, batchSize: UInt32, viaServerName: String?) async throws { + if let error = searchFilterBatchSizeViaServerNameThrowableError { throw error } - searchFilterBatchSizeCallsCount += 1 - searchFilterBatchSizeReceivedArguments = (filter: filter, batchSize: batchSize) + searchFilterBatchSizeViaServerNameCallsCount += 1 + searchFilterBatchSizeViaServerNameReceivedArguments = (filter: filter, batchSize: batchSize, viaServerName: viaServerName) DispatchQueue.main.async { - self.searchFilterBatchSizeReceivedInvocations.append((filter: filter, batchSize: batchSize)) + self.searchFilterBatchSizeViaServerNameReceivedInvocations.append((filter: filter, batchSize: batchSize, viaServerName: viaServerName)) } - try await searchFilterBatchSizeClosure?(filter, batchSize) + try await searchFilterBatchSizeViaServerNameClosure?(filter, batchSize, viaServerName) } } open class RoomListSDKMock: MatrixRustSDK.RoomList { @@ -15710,6 +15775,81 @@ open class RoomListItemSDKMock: MatrixRustSDK.RoomListItem { } } + //MARK: - previewRoom + + open var previewRoomViaThrowableError: Error? + var previewRoomViaUnderlyingCallsCount = 0 + open var previewRoomViaCallsCount: Int { + get { + if Thread.isMainThread { + return previewRoomViaUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = previewRoomViaUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + previewRoomViaUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + previewRoomViaUnderlyingCallsCount = newValue + } + } + } + } + open var previewRoomViaCalled: Bool { + return previewRoomViaCallsCount > 0 + } + open var previewRoomViaReceivedVia: [String]? + open var previewRoomViaReceivedInvocations: [[String]] = [] + + var previewRoomViaUnderlyingReturnValue: RoomPreview! + open var previewRoomViaReturnValue: RoomPreview! { + get { + if Thread.isMainThread { + return previewRoomViaUnderlyingReturnValue + } else { + var returnValue: RoomPreview? = nil + DispatchQueue.main.sync { + returnValue = previewRoomViaUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + previewRoomViaUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + previewRoomViaUnderlyingReturnValue = newValue + } + } + } + } + open var previewRoomViaClosure: (([String]) async throws -> RoomPreview)? + + open override func previewRoom(via: [String]) async throws -> RoomPreview { + if let error = previewRoomViaThrowableError { + throw error + } + previewRoomViaCallsCount += 1 + previewRoomViaReceivedVia = via + DispatchQueue.main.async { + self.previewRoomViaReceivedInvocations.append(via) + } + if let previewRoomViaClosure = previewRoomViaClosure { + return try await previewRoomViaClosure(via) + } else { + return previewRoomViaReturnValue + } + } + //MARK: - roomInfo open var roomInfoThrowableError: Error? @@ -16351,6 +16491,126 @@ open class RoomMessageEventContentWithoutRelationSDKMock: MatrixRustSDK.RoomMess } } } +open class RoomPreviewSDKMock: MatrixRustSDK.RoomPreview { + init() { + super.init(noPointer: .init()) + } + + public required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + fatalError("init(unsafeFromRawPointer:) has not been implemented") + } + + fileprivate var pointer: UnsafeMutableRawPointer! + + //MARK: - info + + open var infoThrowableError: Error? + var infoUnderlyingCallsCount = 0 + open var infoCallsCount: Int { + get { + if Thread.isMainThread { + return infoUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = infoUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + infoUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + infoUnderlyingCallsCount = newValue + } + } + } + } + open var infoCalled: Bool { + return infoCallsCount > 0 + } + + var infoUnderlyingReturnValue: RoomPreviewInfo! + open var infoReturnValue: RoomPreviewInfo! { + get { + if Thread.isMainThread { + return infoUnderlyingReturnValue + } else { + var returnValue: RoomPreviewInfo? = nil + DispatchQueue.main.sync { + returnValue = infoUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + infoUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + infoUnderlyingReturnValue = newValue + } + } + } + } + open var infoClosure: (() throws -> RoomPreviewInfo)? + + open override func info() throws -> RoomPreviewInfo { + if let error = infoThrowableError { + throw error + } + infoCallsCount += 1 + if let infoClosure = infoClosure { + return try infoClosure() + } else { + return infoReturnValue + } + } + + //MARK: - leave + + open var leaveThrowableError: Error? + var leaveUnderlyingCallsCount = 0 + open var leaveCallsCount: Int { + get { + if Thread.isMainThread { + return leaveUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = leaveUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + leaveUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + leaveUnderlyingCallsCount = newValue + } + } + } + } + open var leaveCalled: Bool { + return leaveCallsCount > 0 + } + open var leaveClosure: (() async throws -> Void)? + + open override func leave() async throws { + if let error = leaveThrowableError { + throw error + } + leaveCallsCount += 1 + try await leaveClosure?() + } +} open class SendAttachmentJoinHandleSDKMock: MatrixRustSDK.SendAttachmentJoinHandle { init() { super.init(noPointer: .init()) diff --git a/ElementX/Sources/Services/RoomDirectorySearch/RoomDirectorySearchProxy.swift b/ElementX/Sources/Services/RoomDirectorySearch/RoomDirectorySearchProxy.swift index a07767bce1..7b1a74b63e 100644 --- a/ElementX/Sources/Services/RoomDirectorySearch/RoomDirectorySearchProxy.swift +++ b/ElementX/Sources/Services/RoomDirectorySearch/RoomDirectorySearchProxy.swift @@ -47,7 +47,7 @@ final class RoomDirectorySearchProxy: RoomDirectorySearchProxyProtocol { func search(query: String?) async -> Result { do { - try await roomDirectorySearch.search(filter: query, batchSize: 50) + try await roomDirectorySearch.search(filter: query, batchSize: 50, viaServerName: nil) return .success(()) } catch { return .failure(.searchFailed) diff --git a/project.yml b/project.yml index aeae881b60..1126076d92 100644 --- a/project.yml +++ b/project.yml @@ -60,7 +60,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/element-hq/matrix-rust-components-swift - exactVersion: 1.0.62 + exactVersion: 1.0.63 # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios From 85e1de2c1c74bffdd13005eaeebea1db89561bdc Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:46:19 +0000 Subject: [PATCH 099/114] Update the README. (#3480) --- README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d9ad55c092..244860fffb 100644 --- a/README.md +++ b/README.md @@ -11,19 +11,17 @@ # Element X iOS -ElementX iOS is a [Matrix](https://matrix.org/) iOS Client provided by [Element](https://element.io/). +Element X iOS is a [Matrix](https://matrix.org/) iOS Client provided by [Element](https://element.io/). -The application is a total rewrite of [Element-iOS](https://github.com/element-hq/element-ios) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targetting devices running iOS 16+. +The application is a total rewrite of [Element iOS](https://github.com/element-hq/element-ios) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targeting devices running iOS 17+. ## Rust SDK -ElementX leverages the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) through an FFI layer exposed as a [swift package](https://github.com/matrix-org/matrix-rust-components-swift) that the final client can directly import and use. - -We're doing this as a way to share code between platforms and while we've seen promising results it's still in the experimental stage and bound to change. +Element X leverages the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) through an FFI layer exposed as a [swift package](https://github.com/matrix-org/matrix-rust-components-swift) that the final client can directly import and use. We're doing this as a way to share code between platforms, with [Element X Android](https://github.com/element-hq/element-x-android) using the same SDK. ## Status -This project is in work in progress. The app does not cover yet all functionalities we expect. +This project is in an early rollout & migration phase. ## Contributing @@ -37,9 +35,9 @@ Please refer to the [setting up a development environment](CONTRIBUTING.md#setti ## Support -When you are experiencing an issue on ElementX iOS, please first search in [GitHub issues](https://github.com/element-hq/element-x-ios/issues) +When you are experiencing an issue on Element X iOS, please first search in [GitHub issues](https://github.com/element-hq/element-x-ios/issues) and then in [#element-x-ios:matrix.org](https://matrix.to/#/#element-x-ios:matrix.org). -If after your research you still have a question, ask at [#element-x-ios:matrix.org](https://matrix.to/#/#element-x-ios:matrix.org). Otherwise feel free to create a GitHub issue if you encounter a bug or a crash, by explaining clearly in detail what happened. You can also perform bug reporting (Rageshake) from the Element application by shaking your phone or going to the application settings. This is especially recommended when you encounter a crash. +If after your research you still have a question, ask at [#element-x-ios:matrix.org](https://matrix.to/#/#element-x-ios:matrix.org). Otherwise feel free to create a GitHub issue if you encounter a bug or a crash, by explaining clearly in detail what happened. You can also perform bug reporting (Rageshake) from the Element application by going to the application settings. This is especially recommended when you encounter a crash. ## Forking From 8e26718d0bc7ee9729107b8c89e71e354ae2cbdb Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 4 Nov 2024 14:22:50 +0000 Subject: [PATCH 100/114] Encryption Flow Coordinators. (#3471) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Manage the secure backup screens with flow coordinators. * Add UI tests for the EncryptionSettingsFlowCoordinator. * Realise that the settings flow can't reset anymore and remove the sub-flow 🤦‍♂️ * Add UI tests for the EncryptionResetFlowCoordinator. --- ElementX.xcodeproj/project.pbxproj | 24 +++ .../EncryptionResetFlowCoordinator.swift | 158 ++++++++++++++ .../EncryptionSettingsFlowCoordinator.swift | 198 ++++++++++++++++++ .../OnboardingFlowCoordinator.swift | 44 ++-- .../SettingsFlowCoordinator.swift | 28 +-- ElementX/Sources/Mocks/ClientProxyMock.swift | 10 +- .../SDK/IdentityResetHandleSDKMock.swift | 22 ++ .../Mocks/SecureBackupControllerMock.swift | 48 +++++ .../Other/AccessibilityIdentifiers.swift | 31 +++ ...yptionResetPasswordScreenCoordinator.swift | 21 +- .../EncryptionResetPasswordScreenModels.swift | 13 +- ...cryptionResetPasswordScreenViewModel.swift | 11 +- .../View/EncryptionResetPasswordScreen.swift | 10 +- .../EncryptionResetScreenCoordinator.swift | 27 +-- .../EncryptionResetScreenModels.swift | 3 +- .../EncryptionResetScreenViewModel.swift | 16 +- ...cryptionResetScreenViewModelProtocol.swift | 1 - .../View/EncryptionResetScreen.swift | 1 + .../View/SecureBackupKeyBackupScreen.swift | 1 + ...reBackupRecoveryKeyScreenCoordinator.swift | 32 +-- .../SecureBackupRecoveryKeyScreenModels.swift | 2 - ...cureBackupRecoveryKeyScreenViewModel.swift | 8 +- .../View/SecureBackupRecoveryKeyScreen.swift | 5 + .../SecureBackupScreenCoordinator.swift | 92 +------- .../SecureBackupScreenModels.swift | 4 +- .../SecureBackupScreenViewModel.swift | 4 +- .../View/SecureBackupScreen.swift | 20 +- .../UITests/UITestsAppCoordinator.swift | 34 +++ .../UITests/UITestsScreenIdentifier.swift | 3 + UITests/Sources/EncryptionResetUITests.swift | 37 ++++ .../Sources/EncryptionSettingsUITests.swift | 80 +++++++ ...nReset-0-iPad-10th-generation-en-GB.UI.png | 3 + .../encryptionReset-0-iPhone-16-en-GB.UI.png | 3 + ...nReset-1-iPad-10th-generation-en-GB.UI.png | 3 + .../encryptionReset-1-iPhone-16-en-GB.UI.png | 3 + ...nReset-2-iPad-10th-generation-en-GB.UI.png | 3 + .../encryptionReset-2-iPhone-16-en-GB.UI.png | 3 + ...ttings-0-iPad-10th-generation-en-GB.UI.png | 3 + ...ncryptionSettings-0-iPhone-16-en-GB.UI.png | 3 + ...ttings-1-iPad-10th-generation-en-GB.UI.png | 3 + ...ncryptionSettings-1-iPhone-16-en-GB.UI.png | 3 + ...ttings-2-iPad-10th-generation-en-GB.UI.png | 3 + ...ncryptionSettings-2-iPhone-16-en-GB.UI.png | 3 + ...ttings-3-iPad-10th-generation-en-GB.UI.png | 3 + ...ncryptionSettings-3-iPhone-16-en-GB.UI.png | 3 + ...ttings-4-iPad-10th-generation-en-GB.UI.png | 3 + ...ncryptionSettings-4-iPhone-16-en-GB.UI.png | 3 + ...ttings-5-iPad-10th-generation-en-GB.UI.png | 3 + ...ncryptionSettings-5-iPhone-16-en-GB.UI.png | 3 + ...ttings-6-iPad-10th-generation-en-GB.UI.png | 3 + ...ncryptionSettings-6-iPhone-16-en-GB.UI.png | 3 + 51 files changed, 822 insertions(+), 226 deletions(-) create mode 100644 ElementX/Sources/FlowCoordinators/EncryptionResetFlowCoordinator.swift create mode 100644 ElementX/Sources/FlowCoordinators/EncryptionSettingsFlowCoordinator.swift create mode 100644 ElementX/Sources/Mocks/SDK/IdentityResetHandleSDKMock.swift create mode 100644 ElementX/Sources/Mocks/SecureBackupControllerMock.swift create mode 100644 UITests/Sources/EncryptionResetUITests.swift create mode 100644 UITests/Sources/EncryptionSettingsUITests.swift create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionReset-0-iPad-10th-generation-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionReset-0-iPhone-16-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionReset-1-iPad-10th-generation-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionReset-1-iPhone-16-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionReset-2-iPad-10th-generation-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionReset-2-iPhone-16-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionSettings-0-iPad-10th-generation-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionSettings-0-iPhone-16-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionSettings-1-iPad-10th-generation-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionSettings-1-iPhone-16-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionSettings-2-iPad-10th-generation-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionSettings-2-iPhone-16-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionSettings-3-iPad-10th-generation-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionSettings-3-iPhone-16-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionSettings-4-iPad-10th-generation-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionSettings-4-iPhone-16-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionSettings-5-iPad-10th-generation-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionSettings-5-iPhone-16-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionSettings-6-iPad-10th-generation-en-GB.UI.png create mode 100644 UITests/Sources/__Snapshots__/Application/encryptionSettings-6-iPhone-16-en-GB.UI.png diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index bab5fb81f7..0c81278f56 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -158,6 +158,7 @@ 21F29351EDD7B2A5534EE862 /* SecureBackupKeyBackupScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD558A898847C179E4B7A237 /* SecureBackupKeyBackupScreen.swift */; }; 22882C710BC99EC34A5024A0 /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; }; 22C5483D01EEB290B8339817 /* HomeScreenInviteCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FC33C3F6BF597E095CE9FA /* HomeScreenInviteCell.swift */; }; + 230981086F0199F913434D6B /* EncryptionSettingsUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BF8E5D4C95974B96A18C80E /* EncryptionSettingsUITests.swift */; }; 2335D1AB954C151FD8779F45 /* RoomPermissionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0096BC5DA86AF6B6E5742AC /* RoomPermissionsTests.swift */; }; 23701DE32ACD6FD40AA992C3 /* MediaUploadingPreprocessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE203026B9AD3DB412439866 /* MediaUploadingPreprocessorTests.swift */; }; 237FC70AA257B935F53316BA /* SessionVerificationControllerProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C55D7E514F9DE4E3D72FDCAD /* SessionVerificationControllerProxy.swift */; }; @@ -205,6 +206,7 @@ 2C5E832434EE94E21AB3B238 /* EmojiPickerScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EAE3E9D5EF4A6D5D9C6CFD /* EmojiPickerScreenViewModel.swift */; }; 2CA61BB208CD82EBDB58CD13 /* VideoRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0CBEAB5F796BEFBAF7BB6A /* VideoRoomTimelineView.swift */; }; 2CA6ABBC9A88EB89EA52FCCB /* ConfettiScene.scn in Resources */ = {isa = PBXBuildFile; fileRef = B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */; }; + 2D0E3983288E2D35613AD681 /* SecureBackupControllerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AB29A2D95D3469B5F016655 /* SecureBackupControllerMock.swift */; }; 2D2D8A53B35BE8D8A01449C6 /* PinnedEventsBannerStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FA38E813BE14149F173F461 /* PinnedEventsBannerStateTests.swift */; }; 2DA27D78560D5F79B917E163 /* AudioConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E44E35AA87F49503E7B3BF6E /* AudioConverter.swift */; }; 2DD9D0FE7CB5CFC80D071451 /* AppLockScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3E67E09FE5A35D73818C39 /* AppLockScreenModels.swift */; }; @@ -353,6 +355,7 @@ 4CE638FD837ED72CD98AD9A9 /* AppHooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E4AB573FAEBB7B853DD04C /* AppHooks.swift */; }; 4D0F4385B7DDB68C66C78857 /* FormattedBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = C258C9C815272911A5B132C3 /* FormattedBodyText.swift */; }; 4D23D41B8109E010304050F8 /* QRCodeLoginScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA551A98778CEE7366838CE2 /* QRCodeLoginScreenCoordinator.swift */; }; + 4D2B54233C7B2C04B4ABE55A /* EncryptionSettingsFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECB836DD8BE31931F51B8AC9 /* EncryptionSettingsFlowCoordinator.swift */; }; 4D4D236F0BBCDC4D2CBCCBB5 /* RoomChangePermissionsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = C729D95CB4588D4D9AAC3DFA /* RoomChangePermissionsScreenModels.swift */; }; 4DAEE2468669848B6C9F55B4 /* TimelineReadReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33035418BB35754232985871 /* TimelineReadReceiptsView.swift */; }; 4DEEFB73181C3B023DB42686 /* NetworkMonitorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1575947B7A6FE08C57FE5EE4 /* NetworkMonitorProtocol.swift */; }; @@ -448,6 +451,7 @@ 64D05250CEDE8B604119F6E6 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 981663D961C94270FA035FD0 /* Alert.swift */; }; 64E541F88F35BD126C4AFCA1 /* AppLockScreenPINKeypad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38345442415E07A931197C55 /* AppLockScreenPINKeypad.swift */; }; 64EE9D2CF7AD02EE53983CE1 /* FileRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F75EF13F49DD2204E760910 /* FileRoomTimelineView.swift */; }; + 64F8590F4BEE4DA231F97D83 /* EncryptionResetFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A07B011547B201A836C03052 /* EncryptionResetFlowCoordinator.swift */; }; 651341E67C3514F9811A1EC1 /* LoginScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05F598B1B346DAF223651C91 /* LoginScreenCoordinator.swift */; }; 652ACCF104A8CEF30788963C /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1423AB065857FA546444DB15 /* NotificationManager.swift */; }; 6530865EB9A8C0F0AF0216DA /* ServerSelectionScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9501D11B4258DFA33BA3B40F /* ServerSelectionScreenModels.swift */; }; @@ -917,6 +921,7 @@ C9BE065FA7D4E77E4C61CB69 /* MapLibreModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B81B6170DB690013CEB646F4 /* MapLibreModels.swift */; }; C9F5B48D15B9BCAE1F8D564E /* RoomNotificationModeProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1511766C534367700C8DD75 /* RoomNotificationModeProxy.swift */; }; CA12AE0DCD57D49CD96C699A /* WaveformCursorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB9EABCA9348DFA27439A809 /* WaveformCursorView.swift */; }; + CACD1352927336F01FC76612 /* EncryptionResetUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE4C76F31A382B8E4DD07583 /* EncryptionResetUITests.swift */; }; CB137BFB3E083C33E398A6CB /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 020597E28A4BC8E1BE8EDF6E /* KeychainAccess */; }; CB498F4E27AA0545DCEF0F6F /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 36B7FC232711031AA2B0D188 /* DTCoreText */; }; CB6956565D858C523E3E3B16 /* ComposerDraftServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7E37072597F67C4DD8CC2DB /* ComposerDraftServiceProtocol.swift */; }; @@ -1149,6 +1154,7 @@ FF34BF2AF731340AF9414A18 /* SwipeRightAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4552D3466B1453F287223ADA /* SwipeRightAction.swift */; }; FF7E8ECC8E7E1D1851517536 /* PollFormScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 347D708104CCEF771428C9A3 /* PollFormScreenViewModelTests.swift */; }; FF9C06BBF6AC6F1CFFBEBFFC /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 90791B9C739C716A40E1B230 /* target.yml */; }; + FFD52DCDA6962055A363CC8F /* IdentityResetHandleSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EFB1D29B0870AFB6A56E9B8 /* IdentityResetHandleSDKMock.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -1475,6 +1481,7 @@ 3BAC027034248429A438886B /* AppMediatorMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediatorMock.swift; sourceTree = ""; }; 3BC1B7CB061C9865B2B91B56 /* QRCodeLoginScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeLoginScreenViewModel.swift; sourceTree = ""; }; 3BDCCD2F6B405C14B9BCE94E /* JoinRoomScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenCoordinator.swift; sourceTree = ""; }; + 3BF8E5D4C95974B96A18C80E /* EncryptionSettingsUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionSettingsUITests.swift; sourceTree = ""; }; 3C368CAB3063EF275357ECD4 /* LoginScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenViewModel.swift; sourceTree = ""; }; 3C3E67E09FE5A35D73818C39 /* AppLockScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenModels.swift; sourceTree = ""; }; 3CCD41CD67DB5DA0D436BFE9 /* VoiceMessageRoomPlaybackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomPlaybackView.swift; sourceTree = ""; }; @@ -1553,6 +1560,7 @@ 49E751D7EDB6043238111D90 /* UNNotificationRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UNNotificationRequest.swift; sourceTree = ""; }; 4A2B5274C1D3D2999D643786 /* EncryptionResetPasswordScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetPasswordScreenViewModelProtocol.swift; sourceTree = ""; }; 4A5B4CD611DE7E94F5BA87B2 /* AppLockTimerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockTimerTests.swift; sourceTree = ""; }; + 4AB29A2D95D3469B5F016655 /* SecureBackupControllerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupControllerMock.swift; sourceTree = ""; }; 4B2D4EEBE8C098BBADD10939 /* SecureBackupKeyBackupScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupKeyBackupScreenCoordinator.swift; sourceTree = ""; }; 4B41FABA2B0AEF4389986495 /* LoginMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginMode.swift; sourceTree = ""; }; 4BD371B60E07A5324B9507EF /* AnalyticsSettingsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenCoordinator.swift; sourceTree = ""; }; @@ -1632,6 +1640,7 @@ 5E75948AA1FE1D1A7809931F /* AuthenticationServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationServiceProtocol.swift; sourceTree = ""; }; 5E9CBF577B9711CFBB4FA40D /* VoiceMessageRecordingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRecordingView.swift; sourceTree = ""; }; 5EB2CAA266B921D128C35710 /* LegalInformationScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreenCoordinator.swift; sourceTree = ""; }; + 5EFB1D29B0870AFB6A56E9B8 /* IdentityResetHandleSDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityResetHandleSDKMock.swift; sourceTree = ""; }; 5F12E996BFBEB43815189ABF /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = uk.lproj/Localizable.stringsdict; sourceTree = ""; }; 5F4134FEFE4EB55759017408 /* UserSessionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionProtocol.swift; sourceTree = ""; }; 5FACD034DB52525A3CEF2BDF /* SessionVerificationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreen.swift; sourceTree = ""; }; @@ -1906,6 +1915,7 @@ A019A12C866D64CF072024B9 /* AppLockSetupPINScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenViewModel.swift; sourceTree = ""; }; A02D1A490944BF01A37586E1 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/SAS.strings; sourceTree = ""; }; A057F2FDC14866C3026A89A4 /* NotificationManagerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManagerProtocol.swift; sourceTree = ""; }; + A07B011547B201A836C03052 /* EncryptionResetFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetFlowCoordinator.swift; sourceTree = ""; }; A103580EBA06155B1343EF16 /* HomeScreenKnockedCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenKnockedCell.swift; sourceTree = ""; }; A12D3B1BCF920880CA8BBB6B /* UserIndicatorControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorControllerProtocol.swift; sourceTree = ""; }; A130A2251A15A7AACC84FD37 /* RoomPollsHistoryScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreenViewModelProtocol.swift; sourceTree = ""; }; @@ -2035,6 +2045,7 @@ BC8AA23D4F37CC26564F63C5 /* LayoutMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutMocks.swift; sourceTree = ""; }; BCDA016D05107DED3B9495CB /* TimelineItemDebugView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemDebugView.swift; sourceTree = ""; }; BE148A4FFEE853C5A281500C /* UNNotificationContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UNNotificationContent.swift; sourceTree = ""; }; + BE4C76F31A382B8E4DD07583 /* EncryptionResetUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetUITests.swift; sourceTree = ""; }; BE78CAD0B964C66FD06EF83E /* DeactivateAccountScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeactivateAccountScreenModels.swift; sourceTree = ""; }; BE89A8BD65CCE3FCC925CA14 /* TimelineItemReplyDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemReplyDetails.swift; sourceTree = ""; }; BE9BBB18FB27F09032AD8769 /* NotificationPermissionsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPermissionsScreenViewModel.swift; sourceTree = ""; }; @@ -2244,6 +2255,7 @@ EBEB8D9F4940E161B18FE4BC /* UITestsNotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestsNotificationCenter.swift; sourceTree = ""; }; EC589E641AE46EFB2962534D /* RoomMemberDetailsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsViewModelTests.swift; sourceTree = ""; }; EC5D7DA665E1F5F509C994C7 /* ScaledOffsetModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScaledOffsetModifier.swift; sourceTree = ""; }; + ECB836DD8BE31931F51B8AC9 /* EncryptionSettingsFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionSettingsFlowCoordinator.swift; sourceTree = ""; }; ECD5FCBA169B6A82F501CA1B /* AnalyticsSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenViewModelProtocol.swift; sourceTree = ""; }; ECF79FB25E2D4BD6F50CE7C9 /* RoomMembersListScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListScreenViewModel.swift; sourceTree = ""; }; ED003DF1B7CF40E7073A2280 /* TracingConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracingConfiguration.swift; sourceTree = ""; }; @@ -2935,6 +2947,7 @@ FC83F47D2173B7538AA72E0E /* RoomSummaryProviderMock.swift */, D479DF730528153665E5782E /* RoomTimelineControllerFactoryMock.swift */, F74532E01B317C56C1BE8FA8 /* RoomTimelineProviderMock.swift */, + 4AB29A2D95D3469B5F016655 /* SecureBackupControllerMock.swift */, 248649EBA5BC33DB93698734 /* SessionVerificationControllerProxyMock.swift */, 7893780A1FD6E3F38B3E9049 /* UserIndicatorControllerMock.swift */, AAD01F7FC2BBAC7351948595 /* UserProfile+Mock.swift */, @@ -3531,6 +3544,8 @@ 0DBB08A95EFA668F2CF27211 /* AppLockSetupFlowCoordinator.swift */, A9B069D7772DDF6513E0F1B8 /* AuthenticationFlowCoordinator.swift */, 7367B3B9A8CAF902220F31D1 /* BugReportFlowCoordinator.swift */, + A07B011547B201A836C03052 /* EncryptionResetFlowCoordinator.swift */, + ECB836DD8BE31931F51B8AC9 /* EncryptionSettingsFlowCoordinator.swift */, C3285BD95B564CA2A948E511 /* OnboardingFlowCoordinator.swift */, A54AAF72E821B4084B7E4298 /* PinnedEventsTimelineFlowCoordinator.swift */, 9A008E57D52B07B78DFAD1BB /* RoomFlowCoordinator.swift */, @@ -4444,6 +4459,8 @@ 295E28C3B9EAADF519BF2F44 /* AuthenticationFlowCoordinatorUITests.swift */, C6FEA87EA3752203065ECE27 /* BugReportUITests.swift */, F8CEB4634C0DD7779C4AB504 /* CreateRoomScreenUITests.swift */, + BE4C76F31A382B8E4DD07583 /* EncryptionResetUITests.swift */, + 3BF8E5D4C95974B96A18C80E /* EncryptionSettingsUITests.swift */, 3368395F06AA180138E185B6 /* PollFormScreenUITests.swift */, C5B7A755E985FA14469E86B2 /* RoomMembersListScreenUITests.swift */, 45571C2EBD98ED7E0CEA7AF7 /* RoomRolesAndPermissionsUITests.swift */, @@ -5287,6 +5304,7 @@ isa = PBXGroup; children = ( 8EAF4A49F3ACD8BB8B0D2371 /* ClientSDKMock.swift */, + 5EFB1D29B0870AFB6A56E9B8 /* IdentityResetHandleSDKMock.swift */, E8DE9D0D480D087D0F676B52 /* UserIdentitySDKMock.swift */, ); path = SDK; @@ -6470,6 +6488,7 @@ 03CDCA6243F89B194E3FAD17 /* EncryptionAuthenticity.swift in Sources */, FBD402E3170EB1ED0D1AA672 /* EncryptionKeyProvider.swift in Sources */, 46A6DB0F78FB399BD59E2D41 /* EncryptionKeyProviderProtocol.swift in Sources */, + 64F8590F4BEE4DA231F97D83 /* EncryptionResetFlowCoordinator.swift in Sources */, 0C6DF318E9C8F6461E6ABDE7 /* EncryptionResetPasswordScreen.swift in Sources */, 36926D795D6D19177C7812F8 /* EncryptionResetPasswordScreenCoordinator.swift in Sources */, B1B255CE0E4306DD6E09D936 /* EncryptionResetPasswordScreenModels.swift in Sources */, @@ -6480,6 +6499,7 @@ 97BAEDD9054FB5F233EE928B /* EncryptionResetScreenModels.swift in Sources */, EC3320639828BED8B3E5F2C6 /* EncryptionResetScreenViewModel.swift in Sources */, A0868BDE84D2140A885BE3C9 /* EncryptionResetScreenViewModelProtocol.swift in Sources */, + 4D2B54233C7B2C04B4ABE55A /* EncryptionSettingsFlowCoordinator.swift in Sources */, 50539366B408780B232C1910 /* EstimatedWaveformView.swift in Sources */, F78BAD28482A467287A9A5A3 /* EventBasedMessageTimelineItemProtocol.swift in Sources */, 02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */, @@ -6527,6 +6547,7 @@ D22345698F6548C1EE960940 /* IdentityConfirmedScreenModels.swift in Sources */, 01681E8B20AD6F0D237F2DC1 /* IdentityConfirmedScreenViewModel.swift in Sources */, AADE7C2497A7B55D8BED7BD6 /* IdentityConfirmedScreenViewModelProtocol.swift in Sources */, + FFD52DCDA6962055A363CC8F /* IdentityResetHandleSDKMock.swift in Sources */, BA31448FBD9697F8CB9A83CD /* ImageCache.swift in Sources */, 7CD16990BA843BE9ED639129 /* ImageRoomTimelineItem.swift in Sources */, B796A25F282C0A340D1B9C12 /* ImageRoomTimelineItemContent.swift in Sources */, @@ -6868,6 +6889,7 @@ 67160204A8D362BB7D4AD259 /* Search.swift in Sources */, 339BC18777912E1989F2F17D /* Section.swift in Sources */, F833D5B5BE6707F961FA88DB /* SecureBackupController.swift in Sources */, + 2D0E3983288E2D35613AD681 /* SecureBackupControllerMock.swift in Sources */, 0C88044649BAEE6C49BFC43A /* SecureBackupControllerProtocol.swift in Sources */, 21F29351EDD7B2A5534EE862 /* SecureBackupKeyBackupScreen.swift in Sources */, 5BC6C4ADFE7F2A795ECDE130 /* SecureBackupKeyBackupScreenCoordinator.swift in Sources */, @@ -7092,6 +7114,8 @@ 7756C4E90CABE6F14F7920A0 /* BugReportUITests.swift in Sources */, 94D0F36A87E596A93C0C178A /* Bundle.swift in Sources */, 9F19096BFA629C0AC282B1E4 /* CreateRoomScreenUITests.swift in Sources */, + CACD1352927336F01FC76612 /* EncryptionResetUITests.swift in Sources */, + 230981086F0199F913434D6B /* EncryptionSettingsUITests.swift in Sources */, 0CF81807BE5FBFC9E2BBCECF /* PollFormScreenUITests.swift in Sources */, 44121202B4A260C98BF615A7 /* RoomMembersListScreenUITests.swift in Sources */, D29E046C1E3045E0346C479D /* RoomRolesAndPermissionsUITests.swift in Sources */, diff --git a/ElementX/Sources/FlowCoordinators/EncryptionResetFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/EncryptionResetFlowCoordinator.swift new file mode 100644 index 0000000000..aea6264c49 --- /dev/null +++ b/ElementX/Sources/FlowCoordinators/EncryptionResetFlowCoordinator.swift @@ -0,0 +1,158 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Combine +import Foundation +import SwiftState + +enum EncryptionResetFlowCoordinatorAction: Equatable { + /// The flow is complete. + case resetComplete + /// The flow was cancelled. + case cancel +} + +struct EncryptionResetFlowCoordinatorParameters { + let userSession: UserSessionProtocol + let userIndicatorController: UserIndicatorControllerProtocol + let navigationStackCoordinator: NavigationStackCoordinator + let windowManger: WindowManagerProtocol +} + +class EncryptionResetFlowCoordinator: FlowCoordinatorProtocol { + private let userSession: UserSessionProtocol + private let userIndicatorController: UserIndicatorControllerProtocol + + private let navigationStackCoordinator: NavigationStackCoordinator + private let windowManager: WindowManagerProtocol + + enum State: StateType { + /// The state machine hasn't started. + case initial + /// The root screen for this flow. + case encryptionResetScreen + /// Confirming the user's password to continue. + case confirmingPassword + } + + enum Event: EventType { + /// The flow is being started. + case start + + /// The user needs to confirm their password to reset. + case confirmPassword + /// The user confirmed their password. + case finishedConfirmingPassword + } + + private let stateMachine: StateMachine + private var cancellables: Set = [] + + private let actionsSubject: PassthroughSubject = .init() + var actionsPublisher: AnyPublisher { + actionsSubject.eraseToAnyPublisher() + } + + init(parameters: EncryptionResetFlowCoordinatorParameters) { + userSession = parameters.userSession + userIndicatorController = parameters.userIndicatorController + navigationStackCoordinator = parameters.navigationStackCoordinator + windowManager = parameters.windowManger + + stateMachine = .init(state: .initial) + configureStateMachine() + } + + func start() { + stateMachine.tryEvent(.start) + } + + func handleAppRoute(_ appRoute: AppRoute, animated: Bool) { + // There aren't any routes to this screen, so always clear the stack. + clearRoute(animated: animated) + } + + func clearRoute(animated: Bool) { + // As we push screens on top of an existing stack, popping to root wouldn't be safe. + switch stateMachine.state { + case .initial: + break + case .encryptionResetScreen: + navigationStackCoordinator.pop(animated: animated) + case .confirmingPassword: + navigationStackCoordinator.pop(animated: animated) // Password screen. + navigationStackCoordinator.pop(animated: animated) // EncryptionReset screen. + } + } + + // MARK: - Private + + private func configureStateMachine() { + stateMachine.addRoutes(event: .start, transitions: [.initial => .encryptionResetScreen]) { [weak self] _ in + self?.presentEncryptionResetScreen() + } + + stateMachine.addRoutes(event: .confirmPassword, transitions: [.encryptionResetScreen => .confirmingPassword]) { [weak self] context in + guard let passwordPublisher = context.userInfo as? PassthroughSubject else { fatalError("Expected a publisher in the userInfo.") } + self?.presentPasswordScreen(passwordPublisher: passwordPublisher) + } + stateMachine.addRoutes(event: .finishedConfirmingPassword, transitions: [.confirmingPassword => .encryptionResetScreen]) + + stateMachine.addErrorHandler { context in + fatalError("Unexpected transition: \(context)") + } + } + + private func presentEncryptionResetScreen() { + let coordinator = EncryptionResetScreenCoordinator(parameters: .init(clientProxy: userSession.clientProxy, + userIndicatorController: userIndicatorController)) + + coordinator.actionsPublisher.sink { [weak self] action in + guard let self else { return } + + switch action { + case .requestOIDCAuthorisation(let url): + presentOIDCAuthorization(for: url) + case .requestPassword(let passwordPublisher): + stateMachine.tryEvent(.confirmPassword, userInfo: passwordPublisher) + case .cancel: + actionsSubject.send(.cancel) + case .resetFinished: + actionsSubject.send(.resetComplete) + } + } + .store(in: &cancellables) + + navigationStackCoordinator.setRootCoordinator(coordinator) + } + + private func presentPasswordScreen(passwordPublisher: PassthroughSubject) { + let coordinator = EncryptionResetPasswordScreenCoordinator(parameters: .init(passwordPublisher: passwordPublisher)) + + coordinator.actionsPublisher.sink { [weak self] action in + guard let self else { return } + + switch action { + case .passwordEntered: + navigationStackCoordinator.pop() + } + } + .store(in: &cancellables) + + navigationStackCoordinator.push(coordinator) { [stateMachine] in + stateMachine.tryEvent(.finishedConfirmingPassword) + } + } + + private var accountSettingsPresenter: OIDCAccountSettingsPresenter? + private func presentOIDCAuthorization(for url: URL) { + // Note to anyone in the future if you come back here to make this open in Safari instead of a WAS. + // As of iOS 16, there is an issue on the simulator with accessing the cookie but it works on a device. 🤷‍♂️ + accountSettingsPresenter = OIDCAccountSettingsPresenter(accountURL: url, presentationAnchor: windowManager.mainWindow) + accountSettingsPresenter?.start() + } +} diff --git a/ElementX/Sources/FlowCoordinators/EncryptionSettingsFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/EncryptionSettingsFlowCoordinator.swift new file mode 100644 index 0000000000..af620ef40b --- /dev/null +++ b/ElementX/Sources/FlowCoordinators/EncryptionSettingsFlowCoordinator.swift @@ -0,0 +1,198 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Combine +import Foundation +import SwiftState + +enum EncryptionSettingsFlowCoordinatorAction: Equatable { + /// The flow is complete. + case complete +} + +struct EncryptionSettingsFlowCoordinatorParameters { + let userSession: UserSessionProtocol + let appSettings: AppSettings + let userIndicatorController: UserIndicatorControllerProtocol + let navigationStackCoordinator: NavigationStackCoordinator +} + +class EncryptionSettingsFlowCoordinator: FlowCoordinatorProtocol { + private let userSession: UserSessionProtocol + private let appSettings: AppSettings + private let userIndicatorController: UserIndicatorControllerProtocol + private let navigationStackCoordinator: NavigationStackCoordinator + + // periphery:ignore - retaining purpose + private var encryptionResetFlowCoordinator: EncryptionResetFlowCoordinator? + + enum State: StateType { + /// The state machine hasn't started. + case initial + /// The root screen for this flow. + case secureBackupScreen + /// The user is managing their recovery key. + case recoveryKeyScreen + /// The user is disabling key backups. + case keyBackupScreen + } + + enum Event: EventType { + /// The flow is being started. + case start + + /// The user would like to manage their recovery key. + case manageRecoveryKey + /// The user finished managing their recovery key. + case finishedManagingRecoveryKey + + /// The user doesn't want to use key backup any more. + case disableKeyBackup + /// The key backup screen was dismissed. + case finishedDisablingKeyBackup + } + + private let stateMachine: StateMachine + private var cancellables: Set = [] + + private let actionsSubject: PassthroughSubject = .init() + var actionsPublisher: AnyPublisher { + actionsSubject.eraseToAnyPublisher() + } + + init(parameters: EncryptionSettingsFlowCoordinatorParameters) { + userSession = parameters.userSession + appSettings = parameters.appSettings + userIndicatorController = parameters.userIndicatorController + navigationStackCoordinator = parameters.navigationStackCoordinator + + stateMachine = .init(state: .initial) + configureStateMachine() + } + + func start() { + stateMachine.tryEvent(.start) + } + + func handleAppRoute(_ appRoute: AppRoute, animated: Bool) { + switch appRoute { + case .roomList, .room, .roomAlias, .childRoom, .childRoomAlias, + .roomDetails, .roomMemberDetails, .userProfile, + .event, .eventOnRoomAlias, .childEvent, .childEventOnRoomAlias, + .call, .genericCallLink, .settings: + // These routes aren't in this flow so clear the entire stack. + clearRoute(animated: animated) + case .chatBackupSettings: + popToRootScreen(animated: animated) + } + } + + func clearRoute(animated: Bool) { + let fromState = stateMachine.state + popToRootScreen(animated: animated) + guard fromState != .initial else { return } + navigationStackCoordinator.pop(animated: animated) // SecureBackup screen. + } + + func popToRootScreen(animated: Bool) { + // As we push screens on top of an existing stack, a literal pop to root wouldn't be safe. + switch stateMachine.state { + case .initial, .secureBackupScreen: + break + case .recoveryKeyScreen: + navigationStackCoordinator.setSheetCoordinator(nil, animated: animated) // RecoveryKey screen. + case .keyBackupScreen: + navigationStackCoordinator.setSheetCoordinator(nil, animated: animated) // KeyBackup screen. + } + } + + // MARK: - Private + + private func configureStateMachine() { + stateMachine.addRoutes(event: .start, transitions: [.initial => .secureBackupScreen]) { [weak self] _ in + self?.presentSecureBackupScreen() + } + + stateMachine.addRoutes(event: .manageRecoveryKey, transitions: [.secureBackupScreen => .recoveryKeyScreen]) { [weak self] _ in + self?.presentRecoveryKeyScreen() + } + stateMachine.addRoutes(event: .finishedManagingRecoveryKey, transitions: [.recoveryKeyScreen => .secureBackupScreen]) + + stateMachine.addRoutes(event: .disableKeyBackup, transitions: [.secureBackupScreen => .keyBackupScreen]) { [weak self] _ in + self?.presentKeyBackupScreen() + } + stateMachine.addRoutes(event: .finishedDisablingKeyBackup, transitions: [.keyBackupScreen => .secureBackupScreen]) + + stateMachine.addErrorHandler { context in + fatalError("Unexpected transition: \(context)") + } + } + + private func presentSecureBackupScreen(animated: Bool = true) { + let coordinator = SecureBackupScreenCoordinator(parameters: .init(appSettings: appSettings, + clientProxy: userSession.clientProxy, + userIndicatorController: userIndicatorController)) + coordinator.actions.sink { [weak self] action in + guard let self else { return } + + switch action { + case .manageRecoveryKey: + stateMachine.tryEvent(.manageRecoveryKey) + case .disableKeyBackup: + stateMachine.tryEvent(.disableKeyBackup) + } + } + .store(in: &cancellables) + + navigationStackCoordinator.push(coordinator, animated: animated) { [weak self] in + self?.actionsSubject.send(.complete) + } + } + + private func presentRecoveryKeyScreen() { + let sheetNavigationStackCoordinator = NavigationStackCoordinator() + let coordinator = SecureBackupRecoveryKeyScreenCoordinator(parameters: .init(secureBackupController: userSession.clientProxy.secureBackupController, + userIndicatorController: userIndicatorController, + isModallyPresented: true)) + + coordinator.actions.sink { [weak self] action in + guard let self else { return } + switch action { + case .complete: + navigationStackCoordinator.setSheetCoordinator(nil) + } + } + .store(in: &cancellables) + + sheetNavigationStackCoordinator.setRootCoordinator(coordinator, animated: true) + + navigationStackCoordinator.setSheetCoordinator(sheetNavigationStackCoordinator) { [stateMachine] in + stateMachine.tryEvent(.finishedManagingRecoveryKey) + } + } + + private func presentKeyBackupScreen() { + let sheetNavigationStackCoordinator = NavigationStackCoordinator() + + let coordinator = SecureBackupKeyBackupScreenCoordinator(parameters: .init(secureBackupController: userSession.clientProxy.secureBackupController, + userIndicatorController: userIndicatorController)) + + coordinator.actions.sink { [weak self] action in + switch action { + case .done: + self?.navigationStackCoordinator.setSheetCoordinator(nil) + } + } + .store(in: &cancellables) + + sheetNavigationStackCoordinator.setRootCoordinator(coordinator, animated: true) + + navigationStackCoordinator.setSheetCoordinator(sheetNavigationStackCoordinator) { [stateMachine] in + stateMachine.tryEvent(.finishedDisablingKeyBackup) + } + } +} diff --git a/ElementX/Sources/FlowCoordinators/OnboardingFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/OnboardingFlowCoordinator.swift index 48428c2a8a..cea5f952c5 100644 --- a/ElementX/Sources/FlowCoordinators/OnboardingFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/OnboardingFlowCoordinator.swift @@ -46,6 +46,8 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol { // periphery: ignore - used to store the coordinator to avoid deallocation private var appLockFlowCoordinator: AppLockSetupFlowCoordinator? + // periphery: ignore - used to store the coordinator to avoid deallocation + private var encryptionResetFlowCoordinator: EncryptionResetFlowCoordinator? private let actionsSubject: PassthroughSubject = .init() var actions: AnyPublisher { @@ -251,7 +253,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol { appSettings.hasRunIdentityConfirmationOnboarding = true stateMachine.tryEvent(.nextSkippingIdentityConfimed) case .reset: - presentEncryptionResetScreen() + startEncryptionResetFlow() case .logout: actionsSubject.send(.logout) } @@ -295,12 +297,8 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol { guard let self else { return } switch action { - case .recoveryFixed: + case .complete: break // Moving to next state is Handled by the global session verification listener - case .resetEncryption: - presentEncryptionResetScreen() - default: - MXLog.error("Unexpected recovery action: \(action)") } } .store(in: &cancellables) @@ -308,31 +306,31 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol { presentCoordinator(coordinator) } - private func presentEncryptionResetScreen() { + private func startEncryptionResetFlow() { let resetNavigationStackCoordinator = NavigationStackCoordinator() - - let coordinator = EncryptionResetScreenCoordinator(parameters: .init(clientProxy: userSession.clientProxy, - navigationStackCoordinator: resetNavigationStackCoordinator, - userIndicatorController: userIndicatorController)) + let coordinator = EncryptionResetFlowCoordinator(parameters: .init(userSession: userSession, + userIndicatorController: userIndicatorController, + navigationStackCoordinator: resetNavigationStackCoordinator, + windowManger: windowManager)) coordinator.actionsPublisher.sink { [weak self] action in guard let self else { return } - switch action { - case .cancel: - navigationStackCoordinator.setSheetCoordinator(nil) - case .requestOIDCAuthorisation(let url): - presentOIDCAuthorisationScreen(url: url) - case .resetFinished: + case .resetComplete: // Moving to next state is handled by the global session verification listener navigationStackCoordinator.setSheetCoordinator(nil) + case .cancel: + navigationStackCoordinator.setSheetCoordinator(nil) } } .store(in: &cancellables) - resetNavigationStackCoordinator.setRootCoordinator(coordinator) + encryptionResetFlowCoordinator = coordinator + coordinator.start() - navigationStackCoordinator.setSheetCoordinator(resetNavigationStackCoordinator) + navigationStackCoordinator.setSheetCoordinator(resetNavigationStackCoordinator) { [weak self] in + self?.encryptionResetFlowCoordinator = nil + } } private func presentIdentityConfirmedScreen() { @@ -411,12 +409,4 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol { navigationStackCoordinator.push(coordinator, dismissalCallback: dismissalCallback) } } - - private var accountSettingsPresenter: OIDCAccountSettingsPresenter? - private func presentOIDCAuthorisationScreen(url: URL) { - // Note to anyone in the future if you come back here to make this open in Safari instead of a WAS. - // As of iOS 16, there is an issue on the simulator with accessing the cookie but it works on a device. 🤷‍♂️ - accountSettingsPresenter = OIDCAccountSettingsPresenter(accountURL: url, presentationAnchor: windowManager.mainWindow) - accountSettingsPresenter?.start() - } } diff --git a/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift index 3ae3d31cfa..3ff63c7bbe 100644 --- a/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift @@ -39,9 +39,10 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol { // periphery:ignore - retaining purpose private var appLockSetupFlowCoordinator: AppLockSetupFlowCoordinator? - // periphery:ignore - retaining purpose private var bugReportFlowCoordinator: BugReportFlowCoordinator? + // periphery:ignore - retaining purpose + private var encryptionSettingsFlowCoordinator: EncryptionSettingsFlowCoordinator? private let actionsSubject: PassthroughSubject = .init() var actions: AnyPublisher { @@ -68,7 +69,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol { // The navigation stack doesn't like it if the root and the push happen // on the same loop run DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { - self.presentSecureBackupScreen(animated: animated) + self.startEncryptionSettingsFlow(animated: animated) } default: break @@ -102,7 +103,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol { self.actionsSubject.send(.runLogoutFlow) } case .secureBackup: - presentSecureBackupScreen(animated: true) + startEncryptionSettingsFlow(animated: true) case .userDetails: presentUserDetailsEditScreen() case let .manageAccount(url): @@ -145,21 +146,22 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol { actionsSubject.send(.presentedSettings) } - private func presentSecureBackupScreen(animated: Bool) { - let coordinator = SecureBackupScreenCoordinator(parameters: .init(appSettings: parameters.appSettings, - clientProxy: parameters.userSession.clientProxy, - navigationStackCoordinator: navigationStackCoordinator, - userIndicatorController: parameters.userIndicatorController)) - - coordinator.actions.sink { [weak self] action in + private func startEncryptionSettingsFlow(animated: Bool) { + let coordinator = EncryptionSettingsFlowCoordinator(parameters: .init(userSession: parameters.userSession, + appSettings: parameters.appSettings, + userIndicatorController: parameters.userIndicatorController, + navigationStackCoordinator: navigationStackCoordinator)) + coordinator.actionsPublisher.sink { [weak self] action in switch action { - case .requestOIDCAuthorisation(let url): - self?.presentAccountManagementURL(url) + case .complete: + // The flow coordinator tidies up the stack, no need to do anything. + self?.encryptionSettingsFlowCoordinator = nil } } .store(in: &cancellables) - navigationStackCoordinator.push(coordinator, animated: animated) + encryptionSettingsFlowCoordinator = coordinator + coordinator.start() } private func presentUserDetailsEditScreen() { diff --git a/ElementX/Sources/Mocks/ClientProxyMock.swift b/ElementX/Sources/Mocks/ClientProxyMock.swift index 8cc4962f89..b5b52018bd 100644 --- a/ElementX/Sources/Mocks/ClientProxyMock.swift +++ b/ElementX/Sources/Mocks/ClientProxyMock.swift @@ -13,6 +13,8 @@ struct ClientProxyMockConfiguration { var deviceID: String? var roomSummaryProvider: RoomSummaryProviderProtocol? = RoomSummaryProviderMock(.init()) var roomDirectorySearchProxy: RoomDirectorySearchProxyProtocol? + + var recoveryState: SecureBackupRecoveryState = .enabled } enum ClientProxyMockError: Error { @@ -78,12 +80,8 @@ extension ClientProxyMock { loadMediaThumbnailForSourceWidthHeightThrowableError = ClientProxyError.sdkError(ClientProxyMockError.generic) loadMediaFileForSourceFilenameThrowableError = ClientProxyError.sdkError(ClientProxyMockError.generic) - secureBackupController = { - let secureBackupController = SecureBackupControllerMock() - secureBackupController.underlyingRecoveryState = .init(CurrentValueSubject(.enabled)) - secureBackupController.underlyingKeyBackupState = .init(CurrentValueSubject(.enabled)) - return secureBackupController - }() + secureBackupController = SecureBackupControllerMock(.init(recoveryState: configuration.recoveryState)) + resetIdentityReturnValue = .success(IdentityResetHandleSDKMock(.init())) roomForIdentifierClosure = { [weak self] identifier in guard let room = self?.roomSummaryProvider?.roomListPublisher.value.first(where: { $0.id == identifier }) else { diff --git a/ElementX/Sources/Mocks/SDK/IdentityResetHandleSDKMock.swift b/ElementX/Sources/Mocks/SDK/IdentityResetHandleSDKMock.swift new file mode 100644 index 0000000000..7f4808b2e1 --- /dev/null +++ b/ElementX/Sources/Mocks/SDK/IdentityResetHandleSDKMock.swift @@ -0,0 +1,22 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Foundation +import MatrixRustSDK + +extension IdentityResetHandleSDKMock { + struct Configuration { } + + convenience init(_ configuration: Configuration) { + self.init() + + authTypeReturnValue = .uiaa + resetAuthClosure = { _ in + try await Task.sleep(for: .seconds(60)) + } + } +} diff --git a/ElementX/Sources/Mocks/SecureBackupControllerMock.swift b/ElementX/Sources/Mocks/SecureBackupControllerMock.swift new file mode 100644 index 0000000000..a1c5e0554d --- /dev/null +++ b/ElementX/Sources/Mocks/SecureBackupControllerMock.swift @@ -0,0 +1,48 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Combine +import Foundation + +extension SecureBackupControllerMock { + struct Configuration { + var recoveryState: SecureBackupRecoveryState = .enabled + var keyBackupState: SecureBackupKeyBackupState = .enabled + } + + convenience init(_ configuration: Configuration) { + self.init() + + let recoveryStateSubject = CurrentValueSubject(configuration.recoveryState) + underlyingRecoveryState = .init(recoveryStateSubject) + + let keyBackupStateSubject = CurrentValueSubject(configuration.keyBackupState) + underlyingKeyBackupState = .init(keyBackupStateSubject) + + disableClosure = { + recoveryStateSubject.send(.disabled) + keyBackupStateSubject.send(.unknown) + return .success(()) + } + + enableClosure = { + recoveryStateSubject.send(.disabled) + keyBackupStateSubject.send(.enabled) + return .success(()) + } + + generateRecoveryKeyClosure = { + recoveryStateSubject.send(.enabled) + return .success("a1B2 C3d4 E5F6 g7H8 i9J0 K1l2 M3n4 O5p6 Q7R8 s9T0 U1v2 W3X4") + } + + confirmRecoveryKeyClosure = { _ in + recoveryStateSubject.send(.enabled) + return .success(()) + } + } +} diff --git a/ElementX/Sources/Other/AccessibilityIdentifiers.swift b/ElementX/Sources/Other/AccessibilityIdentifiers.swift index 63eb3aa73f..0c6c95aab4 100644 --- a/ElementX/Sources/Other/AccessibilityIdentifiers.swift +++ b/ElementX/Sources/Other/AccessibilityIdentifiers.swift @@ -16,6 +16,8 @@ enum A11yIdentifiers { static let appLockSetupSettingsScreen = AppLockSetupSettingsScreen() static let bugReportScreen = BugReportScreen() static let changeServerScreen = ChangeServer() + static let encryptionResetScreen = EncryptionResetScreen() + static let encryptionResetPasswordScreen = EncryptionResetPasswordScreen() static let homeScreen = HomeScreen() static let loginScreen = LoginScreen() static let authenticationStartScreen = AuthenticationStartScreen() @@ -24,6 +26,9 @@ enum A11yIdentifiers { static let roomDetailsScreen = RoomDetailsScreen() static let roomNotificationSettingsScreen = RoomNotificationSettingsScreen() static let roomRolesAndPermissionsScreen = RoomRolesAndPermissionsScreen() + static let secureBackupScreen = SecureBackupScreen() + static let secureBackupKeyBackupScreen = SecureBackupKeyBackupScreen() + static let secureBackupRecoveryKeyScreen = SecureBackupRecoveryKeyScreen() static let serverConfirmationScreen = ServerConfirmationScreen() static let sessionVerificationScreen = SessionVerificationScreen() static let settingsScreen = SettingsScreen() @@ -81,6 +86,15 @@ enum A11yIdentifiers { let dismiss = "change_server-dismiss" } + struct EncryptionResetScreen { + let continueReset = "encryption_reset-continue_reset" + } + + struct EncryptionResetPasswordScreen { + let passwordField = "encryption_reset_password-password_field" + let submit = "encryption_reset_password-submit" + } + struct HomeScreen { let userAvatar = "home_screen-user_avatar" let recoveryKeyConfirmationBannerContinue = "home_screen-recovery_key_confirmation_continue" @@ -178,6 +192,23 @@ enum A11yIdentifiers { let memberModeration = "room_roles_and_permissions-member_moderation" } + struct SecureBackupScreen { + let keyStorage = "secure_backup-key_storage" + let recoveryKey = "secure_backup-recovery_key" + } + + struct SecureBackupKeyBackupScreen { + let deleteKeyStorage = "secure_backup_key_backup-delete_key_storage" + } + + struct SecureBackupRecoveryKeyScreen { + let generateRecoveryKey = "secure_backup_recovery_key-generate_recovery_key" + let copyRecoveryKey = "secure_backup_recovery_key-copy_recovery_key" + let done = "secure_backup_recovery_key-done" + let recoveryKeyField = "secure_backup_recovery_key-recovery_key_field" + let confirm = "secure_backup_recovery_key-confirm" + } + struct ServerConfirmationScreen { let `continue` = "server_confirmation-continue" let changeServer = "server_confirmation-change_server" diff --git a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/EncryptionResetPasswordScreenCoordinator.swift b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/EncryptionResetPasswordScreenCoordinator.swift index 3c2a969208..5a6003db20 100644 --- a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/EncryptionResetPasswordScreenCoordinator.swift +++ b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/EncryptionResetPasswordScreenCoordinator.swift @@ -8,17 +8,12 @@ import Combine import SwiftUI -struct EncryptionResetPasswordScreenCoordinatorParameters { } +struct EncryptionResetPasswordScreenCoordinatorParameters { + let passwordPublisher: PassthroughSubject +} -enum EncryptionResetPasswordScreenCoordinatorAction: CustomStringConvertible { - case resetIdentity(String) - - var description: String { - switch self { - case .resetIdentity: - "resetIdentity" - } - } +enum EncryptionResetPasswordScreenCoordinatorAction { + case passwordEntered } final class EncryptionResetPasswordScreenCoordinator: CoordinatorProtocol { @@ -35,7 +30,7 @@ final class EncryptionResetPasswordScreenCoordinator: CoordinatorProtocol { init(parameters: EncryptionResetPasswordScreenCoordinatorParameters) { self.parameters = parameters - viewModel = EncryptionResetPasswordScreenViewModel() + viewModel = EncryptionResetPasswordScreenViewModel(passwordPublisher: parameters.passwordPublisher) } func start() { @@ -44,8 +39,8 @@ final class EncryptionResetPasswordScreenCoordinator: CoordinatorProtocol { guard let self else { return } switch action { - case .resetIdentity(let password): - self.actionsSubject.send(.resetIdentity(password)) + case .passwordEntered: + self.actionsSubject.send(.passwordEntered) } } .store(in: &cancellables) diff --git a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/EncryptionResetPasswordScreenModels.swift b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/EncryptionResetPasswordScreenModels.swift index d228ab4037..d226d29657 100644 --- a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/EncryptionResetPasswordScreenModels.swift +++ b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/EncryptionResetPasswordScreenModels.swift @@ -7,15 +7,8 @@ import Foundation -enum EncryptionResetPasswordScreenViewModelAction: CustomStringConvertible { - case resetIdentity(String) - - var description: String { - switch self { - case .resetIdentity: - "resetIdentity" - } - } +enum EncryptionResetPasswordScreenViewModelAction { + case passwordEntered } struct EncryptionResetPasswordScreenViewState: BindableState { @@ -28,5 +21,5 @@ struct EncryptionResetPasswordScreenViewStateBindings { } enum EncryptionResetPasswordScreenViewAction { - case resetIdentity + case submit } diff --git a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/EncryptionResetPasswordScreenViewModel.swift b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/EncryptionResetPasswordScreenViewModel.swift index ed73d1924f..fd01cfc95b 100644 --- a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/EncryptionResetPasswordScreenViewModel.swift +++ b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/EncryptionResetPasswordScreenViewModel.swift @@ -11,12 +11,16 @@ import SwiftUI typealias EncryptionResetPasswordScreenViewModelType = StateStoreViewModel class EncryptionResetPasswordScreenViewModel: EncryptionResetPasswordScreenViewModelType, EncryptionResetPasswordScreenViewModelProtocol { + private let passwordPublisher: PassthroughSubject + private let actionsSubject: PassthroughSubject = .init() var actionsPublisher: AnyPublisher { actionsSubject.eraseToAnyPublisher() } - init() { + init(passwordPublisher: PassthroughSubject) { + self.passwordPublisher = passwordPublisher + super.init(initialViewState: .init(bindings: .init(password: ""))) } @@ -26,8 +30,9 @@ class EncryptionResetPasswordScreenViewModel: EncryptionResetPasswordScreenViewM MXLog.info("View model: received view action: \(viewAction)") switch viewAction { - case .resetIdentity: - actionsSubject.send(.resetIdentity(state.bindings.password)) + case .submit: + passwordPublisher.send(state.bindings.password) + actionsSubject.send(.passwordEntered) } } } diff --git a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/View/EncryptionResetPasswordScreen.swift b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/View/EncryptionResetPasswordScreen.swift index 03681a81d4..df14c8d134 100644 --- a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/View/EncryptionResetPasswordScreen.swift +++ b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetPasswordScreen/View/EncryptionResetPasswordScreen.swift @@ -5,6 +5,7 @@ // Please see LICENSE in the repository root for full details. // +import Combine import Compound import SwiftUI @@ -32,9 +33,10 @@ struct EncryptionResetPasswordScreen: View { .padding(16) } bottomContent: { Button(L10n.actionResetIdentity, role: .destructive) { - context.send(viewAction: .resetIdentity) + context.send(viewAction: .submit) } .buttonStyle(.compound(.primary)) + .accessibilityIdentifier(A11yIdentifiers.encryptionResetPasswordScreen.submit) } .background() .backgroundStyle(.compound.bgCanvasDefault) @@ -58,8 +60,9 @@ struct EncryptionResetPasswordScreen: View { .focused($textFieldFocus) .submitLabel(.done) .onSubmit { - context.send(viewAction: .resetIdentity) + context.send(viewAction: .submit) } + .accessibilityIdentifier(A11yIdentifiers.encryptionResetPasswordScreen.passwordField) } } } @@ -67,7 +70,8 @@ struct EncryptionResetPasswordScreen: View { // MARK: - Previews struct EncryptionResetPasswordScreen_Previews: PreviewProvider, TestablePreview { - static let viewModel = EncryptionResetPasswordScreenViewModel() + static let passwordPublisher = PassthroughSubject() + static let viewModel = EncryptionResetPasswordScreenViewModel(passwordPublisher: passwordPublisher) static var previews: some View { NavigationStack { EncryptionResetPasswordScreen(context: viewModel.context) diff --git a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/EncryptionResetScreenCoordinator.swift b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/EncryptionResetScreenCoordinator.swift index b2f7edeeab..446a5ab606 100644 --- a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/EncryptionResetScreenCoordinator.swift +++ b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/EncryptionResetScreenCoordinator.swift @@ -9,14 +9,14 @@ import Combine import SwiftUI enum EncryptionResetScreenCoordinatorAction { - case cancel case requestOIDCAuthorisation(URL) + case requestPassword(passwordPublisher: PassthroughSubject) case resetFinished + case cancel } struct EncryptionResetScreenCoordinatorParameters { let clientProxy: ClientProxyProtocol - let navigationStackCoordinator: NavigationStackCoordinator let userIndicatorController: UserIndicatorControllerProtocol } @@ -43,10 +43,10 @@ final class EncryptionResetScreenCoordinator: CoordinatorProtocol { guard let self else { return } switch action { - case .requestPassword: - presentPasswordScreen() case .requestOIDCAuthorisation(let url): self.actionsSubject.send(.requestOIDCAuthorisation(url)) + case .requestPassword(let passwordPublisher): + self.actionsSubject.send(.requestPassword(passwordPublisher: passwordPublisher)) case .resetFinished: self.actionsSubject.send(.resetFinished) case .cancel: @@ -63,23 +63,4 @@ final class EncryptionResetScreenCoordinator: CoordinatorProtocol { func toPresentable() -> AnyView { AnyView(EncryptionResetScreen(context: viewModel.context)) } - - // MARK: - Private - - private func presentPasswordScreen() { - let coordinator = EncryptionResetPasswordScreenCoordinator(parameters: .init()) - - coordinator.actionsPublisher.sink { [weak self] action in - guard let self else { return } - - switch action { - case .resetIdentity(let password): - viewModel.continueResetFlowWith(password: password) - parameters.navigationStackCoordinator.pop() - } - } - .store(in: &cancellables) - - parameters.navigationStackCoordinator.push(coordinator) - } } diff --git a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/EncryptionResetScreenModels.swift b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/EncryptionResetScreenModels.swift index 77ca459153..d85984d452 100644 --- a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/EncryptionResetScreenModels.swift +++ b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/EncryptionResetScreenModels.swift @@ -5,10 +5,11 @@ // Please see LICENSE in the repository root for full details. // +import Combine import Foundation enum EncryptionResetScreenViewModelAction { - case requestPassword + case requestPassword(passwordPublisher: PassthroughSubject) case requestOIDCAuthorisation(url: URL) case resetFinished case cancel diff --git a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/EncryptionResetScreenViewModel.swift b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/EncryptionResetScreenViewModel.swift index ebdfda67bc..a81c2f112c 100644 --- a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/EncryptionResetScreenViewModel.swift +++ b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/EncryptionResetScreenViewModel.swift @@ -21,6 +21,7 @@ class EncryptionResetScreenViewModel: EncryptionResetScreenViewModelType, Encryp } private var identityResetHandle: IdentityResetHandle? + private var passwordCancellable: AnyCancellable? init(clientProxy: ClientProxyProtocol, userIndicatorController: UserIndicatorControllerProtocol) { self.clientProxy = clientProxy @@ -46,12 +47,6 @@ class EncryptionResetScreenViewModel: EncryptionResetScreenViewModelType, Encryp } } - func continueResetFlowWith(password: String) { - Task { - await resetWith(password: password) - } - } - func stop() { Task { await identityResetHandle?.cancel() @@ -80,7 +75,14 @@ class EncryptionResetScreenViewModel: EncryptionResetScreenViewModelType, Encryp switch handle.authType() { case .uiaa: - actionsSubject.send(.requestPassword) + let passwordPublisher = PassthroughSubject() + passwordCancellable = passwordPublisher.sink { [weak self] password in + guard let self else { return } + passwordCancellable = nil + Task { await self.resetWith(password: password) } + } + + actionsSubject.send(.requestPassword(passwordPublisher: passwordPublisher)) case .oidc(let oidcInfo): guard let url = URL(string: oidcInfo.approvalUrl) else { fatalError("Invalid URL received through identity reset handle: \(oidcInfo.approvalUrl)") diff --git a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/EncryptionResetScreenViewModelProtocol.swift b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/EncryptionResetScreenViewModelProtocol.swift index 2ebf141d12..92b48a87d4 100644 --- a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/EncryptionResetScreenViewModelProtocol.swift +++ b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/EncryptionResetScreenViewModelProtocol.swift @@ -12,6 +12,5 @@ protocol EncryptionResetScreenViewModelProtocol { var actionsPublisher: AnyPublisher { get } var context: EncryptionResetScreenViewModelType.Context { get } - func continueResetFlowWith(password: String) func stop() } diff --git a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/View/EncryptionResetScreen.swift b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/View/EncryptionResetScreen.swift index 0bcf831683..e79f243b1e 100644 --- a/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/View/EncryptionResetScreen.swift +++ b/ElementX/Sources/Screens/EncryptionReset/EncryptionResetScreen/View/EncryptionResetScreen.swift @@ -19,6 +19,7 @@ struct EncryptionResetScreen: View { context.send(viewAction: .reset) } .buttonStyle(.compound(.primary)) + .accessibilityIdentifier(A11yIdentifiers.encryptionResetScreen.continueReset) } .background() .backgroundStyle(.compound.bgSubtleSecondary) diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupKeyBackupScreen/View/SecureBackupKeyBackupScreen.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupKeyBackupScreen/View/SecureBackupKeyBackupScreen.swift index 2f7923c01b..4a0de09b95 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupKeyBackupScreen/View/SecureBackupKeyBackupScreen.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupKeyBackupScreen/View/SecureBackupKeyBackupScreen.swift @@ -22,6 +22,7 @@ struct SecureBackupKeyBackupScreen: View { Text(L10n.screenChatBackupKeyBackupActionDisable) } .buttonStyle(.compound(.primary)) + .accessibilityIdentifier(A11yIdentifiers.secureBackupKeyBackupScreen.deleteKeyStorage) } .background() .backgroundStyle(.compound.bgCanvasDefault) diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenCoordinator.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenCoordinator.swift index 0713b3405c..38788a0028 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenCoordinator.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenCoordinator.swift @@ -15,23 +15,22 @@ struct SecureBackupRecoveryKeyScreenCoordinatorParameters { } enum SecureBackupRecoveryKeyScreenCoordinatorAction { - case cancel - case recoverySetUp - case recoveryChanged - case recoveryFixed - case resetEncryption + case complete } final class SecureBackupRecoveryKeyScreenCoordinator: CoordinatorProtocol { + private let parameters: SecureBackupRecoveryKeyScreenCoordinatorParameters private var viewModel: SecureBackupRecoveryKeyScreenViewModelProtocol - private let actionsSubject: PassthroughSubject = .init() + private var cancellables = Set() + private let actionsSubject: PassthroughSubject = .init() var actions: AnyPublisher { actionsSubject.eraseToAnyPublisher() } init(parameters: SecureBackupRecoveryKeyScreenCoordinatorParameters) { + self.parameters = parameters viewModel = SecureBackupRecoveryKeyScreenViewModel(secureBackupController: parameters.secureBackupController, userIndicatorController: parameters.userIndicatorController, isModallyPresented: parameters.isModallyPresented) @@ -44,20 +43,19 @@ final class SecureBackupRecoveryKeyScreenCoordinator: CoordinatorProtocol { guard let self else { return } switch action { case .cancel: - self.actionsSubject.send(.cancel) + self.actionsSubject.send(.complete) case .done(let mode): switch mode { case .setupRecovery: - self.actionsSubject.send(.recoverySetUp) + showSuccessIndicator(title: L10n.screenRecoveryKeySetupSuccess) case .changeRecovery: - self.actionsSubject.send(.recoveryChanged) + showSuccessIndicator(title: L10n.screenRecoveryKeyChangeSuccess) case .fixRecovery: - self.actionsSubject.send(.recoveryFixed) + showSuccessIndicator(title: L10n.screenRecoveryKeyConfirmSuccess) case .unknown: fatalError() } - case .resetEncryption: - self.actionsSubject.send(.resetEncryption) + self.actionsSubject.send(.complete) } } .store(in: &cancellables) @@ -66,4 +64,14 @@ final class SecureBackupRecoveryKeyScreenCoordinator: CoordinatorProtocol { func toPresentable() -> AnyView { AnyView(SecureBackupRecoveryKeyScreen(context: viewModel.context)) } + + // MARK: - Private + + private func showSuccessIndicator(title: String) { + parameters.userIndicatorController.submitIndicator(.init(id: .init(), + type: .modal(progress: .none, interactiveDismissDisabled: false, allowsInteraction: false), + title: title, + iconName: "checkmark", + persistent: false)) + } } diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenModels.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenModels.swift index f04790123c..066577b9df 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenModels.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenModels.swift @@ -10,7 +10,6 @@ import Foundation enum SecureBackupRecoveryKeyScreenViewModelAction { case done(mode: SecureBackupRecoveryKeyScreenViewMode) case cancel - case resetEncryption } enum SecureBackupRecoveryKeyScreenViewMode { @@ -82,7 +81,6 @@ enum SecureBackupRecoveryKeyScreenViewAction { case copyKey case keySaved case confirmKey - case resetEncryption case done case cancel } diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift index 84c31fb8c7..0c3ca4ac1e 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift @@ -78,13 +78,11 @@ class SecureBackupRecoveryKeyScreenViewModel: SecureBackupRecoveryKeyScreenViewM state.bindings.alertInfo = .init(id: .init(), title: L10n.screenRecoveryKeySetupConfirmationTitle, message: L10n.screenRecoveryKeySetupConfirmationDescription, - primaryButton: .init(title: L10n.actionCancel, role: .cancel, action: nil), - secondaryButton: .init(title: L10n.actionContinue, action: { [weak self] in + primaryButton: .init(title: L10n.actionContinue) { [weak self] in guard let self else { return } actionsSubject.send(.done(mode: context.viewState.mode)) - })) - case .resetEncryption: - actionsSubject.send(.resetEncryption) + }, + secondaryButton: .init(title: L10n.actionCancel, role: .cancel, action: nil)) } } diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift index db36934f90..acd39bd180 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift @@ -89,6 +89,7 @@ struct SecureBackupRecoveryKeyScreen: View { } .buttonStyle(.compound(.primary)) .disabled(context.confirmationRecoveryKey.isEmpty) + .accessibilityIdentifier(A11yIdentifiers.secureBackupRecoveryKeyScreen.confirm) } } @@ -111,6 +112,7 @@ struct SecureBackupRecoveryKeyScreen: View { } .buttonStyle(.compound(.primary)) .disabled(context.viewState.recoveryKey == nil || context.viewState.doneButtonEnabled == false) + .accessibilityIdentifier(A11yIdentifiers.secureBackupRecoveryKeyScreen.done) } } @@ -140,6 +142,7 @@ struct SecureBackupRecoveryKeyScreen: View { } .font(.compound.bodyLGSemibold) .padding(.vertical, 11) + .accessibilityIdentifier(A11yIdentifiers.secureBackupRecoveryKeyScreen.generateRecoveryKey) } else { HStack(spacing: 8) { ProgressView() @@ -163,6 +166,7 @@ struct SecureBackupRecoveryKeyScreen: View { } .tint(.compound.iconSecondary) .accessibilityLabel(L10n.actionCopy) + .accessibilityIdentifier(A11yIdentifiers.secureBackupRecoveryKeyScreen.copyRecoveryKey) } } } @@ -204,6 +208,7 @@ struct SecureBackupRecoveryKeyScreen: View { .onSubmit { context.send(viewAction: .confirmKey) } + .accessibilityIdentifier(A11yIdentifiers.secureBackupRecoveryKeyScreen.recoveryKeyField) if let subtitle = context.viewState.recoveryKeySubtitle { Text(subtitle) diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenCoordinator.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenCoordinator.swift index 6605642e8f..52710b9344 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenCoordinator.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenCoordinator.swift @@ -11,12 +11,12 @@ import SwiftUI struct SecureBackupScreenCoordinatorParameters { let appSettings: AppSettings let clientProxy: ClientProxyProtocol - weak var navigationStackCoordinator: NavigationStackCoordinator? let userIndicatorController: UserIndicatorControllerProtocol } enum SecureBackupScreenCoordinatorAction { - case requestOIDCAuthorisation(URL) + case manageRecoveryKey + case disableKeyBackup } final class SecureBackupScreenCoordinator: CoordinatorProtocol { @@ -43,53 +43,10 @@ final class SecureBackupScreenCoordinator: CoordinatorProtocol { guard let self else { return } switch action { - case .recoveryKey: - let recoveryNavigationStackCoordinator = NavigationStackCoordinator() - - let recoveryKeyCoordinator = SecureBackupRecoveryKeyScreenCoordinator(parameters: .init(secureBackupController: parameters.clientProxy.secureBackupController, - userIndicatorController: parameters.userIndicatorController, - isModallyPresented: true)) - - recoveryKeyCoordinator.actions.sink { [weak self] action in - guard let self else { return } - switch action { - case .cancel: - parameters.navigationStackCoordinator?.setSheetCoordinator(nil) - case .recoverySetUp: - showSuccessIndicator(title: L10n.screenRecoveryKeySetupSuccess) - parameters.navigationStackCoordinator?.setSheetCoordinator(nil) - case .recoveryChanged: - showSuccessIndicator(title: L10n.screenRecoveryKeyChangeSuccess) - parameters.navigationStackCoordinator?.setSheetCoordinator(nil) - case .recoveryFixed: - showSuccessIndicator(title: L10n.screenRecoveryKeyConfirmSuccess) - parameters.navigationStackCoordinator?.setSheetCoordinator(nil) - case .resetEncryption: - showEncryptionReset(recoveryNavigationStackCoordinator: recoveryNavigationStackCoordinator) - } - } - .store(in: &cancellables) - - recoveryNavigationStackCoordinator.setRootCoordinator(recoveryKeyCoordinator, animated: true) - - parameters.navigationStackCoordinator?.setSheetCoordinator(recoveryNavigationStackCoordinator) - case .keyBackup: - let navigationStackCoordinator = NavigationStackCoordinator() - - let keyBackupCoordinator = SecureBackupKeyBackupScreenCoordinator(parameters: .init(secureBackupController: parameters.clientProxy.secureBackupController, - userIndicatorController: parameters.userIndicatorController)) - - keyBackupCoordinator.actions.sink { [weak self] action in - switch action { - case .done: - self?.parameters.navigationStackCoordinator?.setSheetCoordinator(nil) - } - } - .store(in: &cancellables) - - navigationStackCoordinator.setRootCoordinator(keyBackupCoordinator, animated: true) - - parameters.navigationStackCoordinator?.setSheetCoordinator(navigationStackCoordinator) + case .manageRecoveryKey: + actionsSubject.send(.manageRecoveryKey) + case .disableKeyBackup: + actionsSubject.send(.disableKeyBackup) } } .store(in: &cancellables) @@ -98,41 +55,4 @@ final class SecureBackupScreenCoordinator: CoordinatorProtocol { func toPresentable() -> AnyView { AnyView(SecureBackupScreen(context: viewModel.context)) } - - // MARK: - Private - - private func showSuccessIndicator(title: String) { - parameters.userIndicatorController.submitIndicator(.init(id: .init(), - type: .modal(progress: .none, interactiveDismissDisabled: false, allowsInteraction: false), - title: title, - iconName: "checkmark", - persistent: false)) - } - - private func showEncryptionReset(recoveryNavigationStackCoordinator: NavigationStackCoordinator) { - let resetNavigationStackCoordinator = NavigationStackCoordinator() - - let coordinator = EncryptionResetScreenCoordinator(parameters: .init(clientProxy: parameters.clientProxy, - navigationStackCoordinator: resetNavigationStackCoordinator, - userIndicatorController: parameters.userIndicatorController)) - - coordinator.actionsPublisher.sink { [weak self] action in - guard let self else { return } - - switch action { - case .cancel: - recoveryNavigationStackCoordinator.setSheetCoordinator(nil) - case .requestOIDCAuthorisation(let url): - actionsSubject.send(.requestOIDCAuthorisation(url)) - case .resetFinished: - parameters.navigationStackCoordinator?.setSheetCoordinator(nil) // Dismiss the recovery screen - recoveryNavigationStackCoordinator.setSheetCoordinator(nil) - } - } - .store(in: &cancellables) - - resetNavigationStackCoordinator.setRootCoordinator(coordinator) - - recoveryNavigationStackCoordinator.setSheetCoordinator(resetNavigationStackCoordinator) - } } diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenModels.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenModels.swift index e0cf773231..85925e073f 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenModels.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenModels.swift @@ -8,8 +8,8 @@ import Foundation enum SecureBackupScreenViewModelAction { - case recoveryKey - case keyBackup + case manageRecoveryKey + case disableKeyBackup } struct SecureBackupScreenViewState: BindableState { diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenViewModel.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenViewModel.swift index 6e3cf495ef..c929f5319a 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenViewModel.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenViewModel.swift @@ -48,7 +48,7 @@ class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackup override func process(viewAction: SecureBackupScreenViewAction) { switch viewAction { case .recoveryKey: - actionsSubject.send(.recoveryKey) + actionsSubject.send(.manageRecoveryKey) case .keyStorageToggled(let enable): let keyBackupState = secureBackupController.keyBackupState.value switch (keyBackupState, enable) { @@ -57,7 +57,7 @@ class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackup enableBackup() case (.enabled, false): state.bindings.keyStorageEnabled = keyBackupState.keyStorageToggleState // Reset the toggle in case the user cancels - actionsSubject.send(.keyBackup) + actionsSubject.send(.disableKeyBackup) default: break } diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift index dbdb8a2317..a0aaa17181 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift @@ -53,7 +53,13 @@ struct SecureBackupScreen: View { .accessibilityElement(children: .combine) }) - keyStorageToggle + ListRow(label: .plain(title: L10n.screenChatBackupKeyStorageToggleTitle, + description: context.viewState.keyStorageToggleDescription), + kind: .toggle($context.keyStorageEnabled)) + .onChange(of: context.keyStorageEnabled) { _, newValue in + context.send(viewAction: .keyStorageToggled(newValue)) + } + .accessibilityIdentifier(A11yIdentifiers.secureBackupScreen.keyStorage) } } @@ -67,15 +73,6 @@ struct SecureBackupScreen: View { return description } - private var keyStorageToggle: some View { - ListRow(label: .plain(title: L10n.screenChatBackupKeyStorageToggleTitle, - description: context.viewState.keyStorageToggleDescription), - kind: .toggle($context.keyStorageEnabled)) - .onChange(of: context.keyStorageEnabled) { _, newValue in - context.send(viewAction: .keyStorageToggled(newValue)) - } - } - private var recoveryKeySection: some View { Section { switch context.viewState.recoveryState { @@ -85,6 +82,7 @@ struct SecureBackupScreen: View { icon: \.key, iconAlignment: .top), kind: .navigationLink { context.send(viewAction: .recoveryKey) }) + .accessibilityIdentifier(A11yIdentifiers.secureBackupScreen.recoveryKey) case .disabled: ListRow(label: .default(title: L10n.screenChatBackupRecoveryActionSetup, description: L10n.screenChatBackupRecoveryActionChangeDescription, @@ -92,10 +90,12 @@ struct SecureBackupScreen: View { iconAlignment: .top), details: .icon(BadgeView(size: 10)), kind: .navigationLink { context.send(viewAction: .recoveryKey) }) + .accessibilityIdentifier(A11yIdentifiers.secureBackupScreen.recoveryKey) case .incomplete: ListRow(label: .plain(title: L10n.screenChatBackupRecoveryActionConfirm), details: .icon(BadgeView(size: 10)), kind: .navigationLink { context.send(viewAction: .recoveryKey) }) + .accessibilityIdentifier(A11yIdentifiers.secureBackupScreen.recoveryKey) default: ListRow(label: .plain(title: L10n.commonLoading), details: .isWaiting(true), kind: .label) } diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index 01cf66c47e..01bea2b808 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -630,6 +630,40 @@ class MockScreen: Identifiable { let navigationStackCoordinator = NavigationStackCoordinator() let coordinator = PollFormScreenCoordinator(parameters: .init(mode: .new)) navigationStackCoordinator.setRootCoordinator(coordinator) + return navigationStackCoordinator + case .encryptionSettings, .encryptionSettingsOutOfSync: + let recoveryState: SecureBackupRecoveryState = id == .encryptionSettings ? .enabled : .incomplete + let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com", recoveryState: recoveryState)) + let userSession = UserSessionMock(.init(clientProxy: clientProxy)) + + let navigationStackCoordinator = NavigationStackCoordinator() + navigationStackCoordinator.setRootCoordinator(BlankFormCoordinator()) + + let coordinator = EncryptionSettingsFlowCoordinator(parameters: .init(userSession: userSession, + appSettings: ServiceLocator.shared.settings, + userIndicatorController: UserIndicatorControllerMock(), + navigationStackCoordinator: navigationStackCoordinator)) + retainedState.append(coordinator) + coordinator.start() + + return navigationStackCoordinator + case .encryptionReset: + let recoveryState: SecureBackupRecoveryState = id == .encryptionSettings ? .enabled : .incomplete + let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com", recoveryState: recoveryState)) + let userSession = UserSessionMock(.init(clientProxy: clientProxy)) + + let userIndicatorController = UserIndicatorController() + userIndicatorController.window = windowManager.overlayWindow + let navigationStackCoordinator = NavigationStackCoordinator() + + let coordinator = EncryptionResetFlowCoordinator(parameters: .init(userSession: userSession, + userIndicatorController: userIndicatorController, + navigationStackCoordinator: navigationStackCoordinator, + windowManger: windowManager)) + + retainedState.append(coordinator) + coordinator.start() + return navigationStackCoordinator case .autoUpdatingTimeline: let appSettings: AppSettings = ServiceLocator.shared.settings diff --git a/ElementX/Sources/UITests/UITestsScreenIdentifier.swift b/ElementX/Sources/UITests/UITestsScreenIdentifier.swift index b725b2060a..74764ee47e 100644 --- a/ElementX/Sources/UITests/UITestsScreenIdentifier.swift +++ b/ElementX/Sources/UITests/UITestsScreenIdentifier.swift @@ -20,6 +20,9 @@ enum UITestsScreenIdentifier: String { case createPoll case createRoom case createRoomNoUsers + case encryptionSettings + case encryptionSettingsOutOfSync + case encryptionReset case roomLayoutBottom case roomLayoutMiddle case roomLayoutTop diff --git a/UITests/Sources/EncryptionResetUITests.swift b/UITests/Sources/EncryptionResetUITests.swift new file mode 100644 index 0000000000..5bc5bea00d --- /dev/null +++ b/UITests/Sources/EncryptionResetUITests.swift @@ -0,0 +1,37 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import XCTest + +@MainActor +class EncryptionResetUITests: XCTestCase { + var app: XCUIApplication! + + @MainActor enum Step { + static let resetScreen = 0 + static let passwordScreen = 1 + static let resetingEncryption = 2 + } + + func testPasswordFlow() async throws { + app = Application.launch(.encryptionReset) + + // Starting with the root screen. + try await app.assertScreenshot(.encryptionReset, step: Step.resetScreen) + + // Confirm the intent to reset. + app.buttons[A11yIdentifiers.encryptionResetScreen.continueReset].tap() + app.buttons[A11yIdentifiers.alertInfo.primaryButton].tap() + try await app.assertScreenshot(.encryptionReset, step: Step.passwordScreen) + + // Enter the password and submit. + let passwordField = app.secureTextFields[A11yIdentifiers.encryptionResetPasswordScreen.passwordField] + passwordField.clearAndTypeText("supersecurepassword", app: app) + app.buttons[A11yIdentifiers.encryptionResetPasswordScreen.submit].tap() + try await app.assertScreenshot(.encryptionReset, step: Step.resetingEncryption) + } +} diff --git a/UITests/Sources/EncryptionSettingsUITests.swift b/UITests/Sources/EncryptionSettingsUITests.swift new file mode 100644 index 0000000000..9b22a6d437 --- /dev/null +++ b/UITests/Sources/EncryptionSettingsUITests.swift @@ -0,0 +1,80 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import XCTest + +@MainActor +class EncryptionSettingsUITests: XCTestCase { + var app: XCUIApplication! + + @MainActor enum Step { + static let secureBackupScreenSetUp = 0 + static let keyBackupScreen = 1 + static let secureBackupScreenDisabled = 2 + static let setUpRecovery = 3 + static let changeRecovery = 4 + + static let secureBackupScreenOutOfSync = 5 + static let confirmRecovery = 6 + } + + func testFlow() async throws { + app = Application.launch(.encryptionSettings) + + // Starting with key storage and recovery enabled. + try await app.assertScreenshot(.encryptionSettings, step: Step.secureBackupScreenSetUp) + + // Toggle key storage off. + app.switches[A11yIdentifiers.secureBackupScreen.keyStorage].tap() + try await app.assertScreenshot(.encryptionSettings, step: Step.keyBackupScreen) + + // Confirm deletion of keys. + app.buttons[A11yIdentifiers.secureBackupKeyBackupScreen.deleteKeyStorage].tap() + app.buttons[A11yIdentifiers.alertInfo.primaryButton].tap() + try await app.assertScreenshot(.encryptionSettings, step: Step.secureBackupScreenDisabled) + + // Toggle key storage back on and set up recovery. + app.switches[A11yIdentifiers.secureBackupScreen.keyStorage].tap() + app.buttons[A11yIdentifiers.secureBackupScreen.recoveryKey].tap() + try await app.assertScreenshot(.encryptionSettings, step: Step.setUpRecovery) + + // Generate and copy a new recovery key. + app.buttons[A11yIdentifiers.secureBackupRecoveryKeyScreen.generateRecoveryKey].tap() + app.buttons[A11yIdentifiers.secureBackupRecoveryKeyScreen.copyRecoveryKey].tap() + app.buttons[A11yIdentifiers.secureBackupRecoveryKeyScreen.done].tap() + app.buttons[A11yIdentifiers.alertInfo.primaryButton].tap() + try await app.assertScreenshot(.encryptionSettings, step: Step.secureBackupScreenSetUp) + + // Change the recovery key. + app.buttons[A11yIdentifiers.secureBackupScreen.recoveryKey].tap() + try await app.assertScreenshot(.encryptionSettings, step: Step.changeRecovery) + + // Generate and copy the updated recovery key. + app.buttons[A11yIdentifiers.secureBackupRecoveryKeyScreen.generateRecoveryKey].tap() + app.buttons[A11yIdentifiers.secureBackupRecoveryKeyScreen.copyRecoveryKey].tap() + app.buttons[A11yIdentifiers.secureBackupRecoveryKeyScreen.done].tap() + app.buttons[A11yIdentifiers.alertInfo.primaryButton].tap() + try await app.assertScreenshot(.encryptionSettings, step: Step.secureBackupScreenSetUp) + } + + func testOutOfSyncFlow() async throws { + app = Application.launch(.encryptionSettingsOutOfSync) + + // Starting with key storage and recovery enabled. + try await app.assertScreenshot(.encryptionSettings, step: Step.secureBackupScreenOutOfSync) + + // Confirm the recovery key. + app.buttons[A11yIdentifiers.secureBackupScreen.recoveryKey].tap() + try await app.assertScreenshot(.encryptionSettings, step: Step.confirmRecovery) + + // Enter the recovery key and submit. + let recoveryKeyField = app.secureTextFields[A11yIdentifiers.secureBackupRecoveryKeyScreen.recoveryKeyField] + recoveryKeyField.clearAndTypeText("sUpe RSec rEtR Ecov ERYk Ey12", app: app) + app.buttons[A11yIdentifiers.secureBackupRecoveryKeyScreen.confirm].tap() + try await app.assertScreenshot(.encryptionSettings, step: Step.secureBackupScreenSetUp) + } +} diff --git a/UITests/Sources/__Snapshots__/Application/encryptionReset-0-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionReset-0-iPad-10th-generation-en-GB.UI.png new file mode 100644 index 0000000000..88c37812eb --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionReset-0-iPad-10th-generation-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:83ebdd64a97d8cef02516cb5be9d5191077c8b3be41252e828a4b83d45004b6e +size 133247 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionReset-0-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionReset-0-iPhone-16-en-GB.UI.png new file mode 100644 index 0000000000..70d68529b1 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionReset-0-iPhone-16-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9dfc381ac69b5e415c4bdf05175ceb6df08cec2db54f3f4b02172eda1f6dd64 +size 162470 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionReset-1-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionReset-1-iPad-10th-generation-en-GB.UI.png new file mode 100644 index 0000000000..c06d19dde7 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionReset-1-iPad-10th-generation-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9720f0cd45534c9699eef06f8a2afd1504d5b51a8773b2b76585002b23d67b77 +size 92574 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionReset-1-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionReset-1-iPhone-16-en-GB.UI.png new file mode 100644 index 0000000000..5b9cf6bc88 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionReset-1-iPhone-16-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7170468705d17c9ba1c6062524d2ccc4cbbd54fd45f1056e53921b20fb6756f9 +size 98637 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionReset-2-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionReset-2-iPad-10th-generation-en-GB.UI.png new file mode 100644 index 0000000000..c1f55d7253 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionReset-2-iPad-10th-generation-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:26acd80deba0bdd9babf133b0b015b6f21942fa02983b921da28925a92573f8e +size 140798 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionReset-2-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionReset-2-iPhone-16-en-GB.UI.png new file mode 100644 index 0000000000..544b27d1cf --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionReset-2-iPhone-16-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3618590fa6e84095976811f53bc283123654470c9aa15babfaec79cc16f19e88 +size 177016 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionSettings-0-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionSettings-0-iPad-10th-generation-en-GB.UI.png new file mode 100644 index 0000000000..12ec2feb44 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionSettings-0-iPad-10th-generation-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:28d3ae3eb6b7ed45c41e1381c052a080906ff767aad74562e09b34b432ba1f06 +size 119892 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionSettings-0-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionSettings-0-iPhone-16-en-GB.UI.png new file mode 100644 index 0000000000..72cb8d5020 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionSettings-0-iPhone-16-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9c7753ecb9a032c14ac1b53ec94dfaf055da89c3d053b0dd095b15b291129730 +size 141912 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionSettings-1-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionSettings-1-iPad-10th-generation-en-GB.UI.png new file mode 100644 index 0000000000..17fb74a505 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionSettings-1-iPad-10th-generation-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:46a5294fe4bf36583b6a7f58602e2ea00761e6027d74e9c982a99f912bf28c26 +size 232922 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionSettings-1-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionSettings-1-iPhone-16-en-GB.UI.png new file mode 100644 index 0000000000..0e8b945fda --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionSettings-1-iPhone-16-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6dc3f0216fa3281196281e997a5cceba862c8427ad352674b860b9d6c16ca02 +size 157003 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionSettings-2-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionSettings-2-iPad-10th-generation-en-GB.UI.png new file mode 100644 index 0000000000..0beffeb38d --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionSettings-2-iPad-10th-generation-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3fbf90b3889e39f75abb39bd5e92ec60dfe18968c85b5ba9c56addf0c4579e6d +size 103188 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionSettings-2-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionSettings-2-iPhone-16-en-GB.UI.png new file mode 100644 index 0000000000..cd9a3e7e26 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionSettings-2-iPhone-16-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d9f4752e15127fd336156f09cd998b96931b1a04f85b3ada3c1cd043d2461195 +size 116493 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionSettings-3-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionSettings-3-iPad-10th-generation-en-GB.UI.png new file mode 100644 index 0000000000..b4c4af1248 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionSettings-3-iPad-10th-generation-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9690d135c26b3bef7c9e1ecd25fe5a45605bf1838f5f3d22f34ef4cc47cf59dc +size 211322 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionSettings-3-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionSettings-3-iPhone-16-en-GB.UI.png new file mode 100644 index 0000000000..f6f18d408e --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionSettings-3-iPhone-16-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3f5e7ccbc806ce6ef88e465be9bb879ef31fd3675822368c93126d66563e07b +size 121844 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionSettings-4-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionSettings-4-iPad-10th-generation-en-GB.UI.png new file mode 100644 index 0000000000..7c91791239 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionSettings-4-iPad-10th-generation-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1bfdba161a6e974df12efd09a48cd905d8162c828c2e6af99eaed74a8631891b +size 210352 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionSettings-4-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionSettings-4-iPhone-16-en-GB.UI.png new file mode 100644 index 0000000000..b89307d812 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionSettings-4-iPhone-16-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:db98e2b1dc6dc2c4f55aa46e783366c69e5ff4521cd0179be0028f47f0387688 +size 119868 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionSettings-5-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionSettings-5-iPad-10th-generation-en-GB.UI.png new file mode 100644 index 0000000000..681c5958c4 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionSettings-5-iPad-10th-generation-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59b95216f4ee18fa2b5c35fa237caf58ac7a976ef70c2a52c6a95ada9966c9c1 +size 76515 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionSettings-5-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionSettings-5-iPhone-16-en-GB.UI.png new file mode 100644 index 0000000000..41ff76b1db --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionSettings-5-iPhone-16-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:745a30111d340511f2be87af6df4ce85ec00baa7d1b085b1082c922337c59ca2 +size 72736 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionSettings-6-iPad-10th-generation-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionSettings-6-iPad-10th-generation-en-GB.UI.png new file mode 100644 index 0000000000..ed948b2911 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionSettings-6-iPad-10th-generation-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca24039f2eacc6517cb7131cfba469fc3db152e7fa28e63307047b170344c17b +size 165666 diff --git a/UITests/Sources/__Snapshots__/Application/encryptionSettings-6-iPhone-16-en-GB.UI.png b/UITests/Sources/__Snapshots__/Application/encryptionSettings-6-iPhone-16-en-GB.UI.png new file mode 100644 index 0000000000..4e47441159 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/encryptionSettings-6-iPhone-16-en-GB.UI.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8b12eda65d7c9b472d156e7a964d6bf962810e881d5310bf04932a05a74a2443 +size 100696 From 6190a03afcc70788213550160b12cb95d4c077ae Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:21:36 +0000 Subject: [PATCH 101/114] Directly show Recovery Key and Encryption Reset screens from the home screen banner. (#3482) --- .../UserSessionFlowCoordinator.swift | 73 ++++++++++++++++++- ...erSessionFlowCoordinatorStateMachine.swift | 30 +++++++- .../HomeScreen/HomeScreenCoordinator.swift | 9 ++- .../Screens/HomeScreen/HomeScreenModels.swift | 6 +- .../HomeScreen/HomeScreenViewModel.swift | 6 +- ...eScreenRecoveryKeyConfirmationBanner.swift | 11 ++- ...firmationBanner-iPad-en-GB.Out-of-sync.png | 4 +- ...irmationBanner-iPad-pseudo.Out-of-sync.png | 4 +- ...tionBanner-iPhone-16-en-GB.Out-of-sync.png | 4 +- ...ionBanner-iPhone-16-pseudo.Out-of-sync.png | 4 +- 10 files changed, 130 insertions(+), 21 deletions(-) diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift index 8e2431e541..42b6f782f3 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift @@ -42,6 +42,9 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { // periphery:ignore - retaining purpose private var bugReportFlowCoordinator: BugReportFlowCoordinator? + // periphery:ignore - retaining purpose + private var encryptionResetFlowCoordinator: EncryptionResetFlowCoordinator? + // periphery:ignore - retaining purpose private var globalSearchScreenCoordinator: GlobalSearchScreenCoordinator? @@ -263,6 +266,16 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { case (.feedbackScreen, .dismissedFeedbackScreen, .roomList): break + case (.roomList, .showRecoveryKeyScreen, .recoveryKeyScreen): + presentRecoveryKeyScreen(animated: animated) + case (.recoveryKeyScreen, .dismissedRecoveryKeyScreen, .roomList): + break + + case (.roomList, .startEncryptionResetFlow, .encryptionResetFlow): + startEncryptionResetFlow(animated: animated) + case (.encryptionResetFlow, .finishedEncryptionResetFlow, .roomList): + break + case (.roomList, .showStartChatScreen, .startChatScreen): presentStartChat(animated: animated) case (.startChatScreen, .dismissedStartChatScreen, .roomList): @@ -453,8 +466,10 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { settingsFlowCoordinator.handleAppRoute(.settings, animated: true) case .presentFeedbackScreen: stateMachine.processEvent(.feedbackScreen) - case .presentSecureBackupSettings: - settingsFlowCoordinator.handleAppRoute(.chatBackupSettings, animated: true) + case .presentRecoveryKeyScreen: + stateMachine.processEvent(.showRecoveryKeyScreen) + case .presentEncryptionResetScreen: + stateMachine.processEvent(.startEncryptionResetFlow) case .presentStartChatScreen: stateMachine.processEvent(.showStartChatScreen) case .presentGlobalSearch: @@ -697,7 +712,59 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { navigationSplitCoordinator.setOverlayCoordinator(nil) } - // MARK: Secure backup confirmation + // MARK: Secure backup + + private func presentRecoveryKeyScreen(animated: Bool) { + let sheetNavigationStackCoordinator = NavigationStackCoordinator() + let parameters = SecureBackupRecoveryKeyScreenCoordinatorParameters(secureBackupController: userSession.clientProxy.secureBackupController, + userIndicatorController: ServiceLocator.shared.userIndicatorController, + isModallyPresented: true) + + let coordinator = SecureBackupRecoveryKeyScreenCoordinator(parameters: parameters) + coordinator.actions.sink { [weak self] action in + guard let self else { return } + switch action { + case .complete: + navigationSplitCoordinator.setSheetCoordinator(nil) + } + } + .store(in: &cancellables) + + sheetNavigationStackCoordinator.setRootCoordinator(coordinator) + + navigationSplitCoordinator.setSheetCoordinator(sheetNavigationStackCoordinator, animated: animated) { [weak self] in + self?.stateMachine.processEvent(.dismissedRecoveryKeyScreen) + } + } + + private func startEncryptionResetFlow(animated: Bool) { + let sheetNavigationStackCoordinator = NavigationStackCoordinator() + let parameters = EncryptionResetFlowCoordinatorParameters(userSession: userSession, + userIndicatorController: ServiceLocator.shared.userIndicatorController, + navigationStackCoordinator: sheetNavigationStackCoordinator, + windowManger: appMediator.windowManager) + + let coordinator = EncryptionResetFlowCoordinator(parameters: parameters) + coordinator.actionsPublisher.sink { [weak self] action in + guard let self else { return } + switch action { + case .resetComplete: + encryptionResetFlowCoordinator = nil + navigationSplitCoordinator.setSheetCoordinator(nil) + case .cancel: + encryptionResetFlowCoordinator = nil + navigationSplitCoordinator.setSheetCoordinator(nil) + } + } + .store(in: &cancellables) + + coordinator.start() + encryptionResetFlowCoordinator = coordinator + + navigationSplitCoordinator.setSheetCoordinator(sheetNavigationStackCoordinator, animated: animated) { [weak self] in + self?.stateMachine.processEvent(.finishedEncryptionResetFlow) + } + } private func presentSecureBackupLogoutConfirmationScreen() { let coordinator = SecureBackupLogoutConfirmationScreenCoordinator(parameters: .init(secureBackupController: userSession.clientProxy.secureBackupController, diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinatorStateMachine.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinatorStateMachine.swift index 96189d7b12..5fe2d18c6f 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinatorStateMachine.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinatorStateMachine.swift @@ -24,6 +24,12 @@ class UserSessionFlowCoordinatorStateMachine { /// Showing the settings screen case settingsScreen(selectedRoomID: String?) + /// Showing the recovery key screen. + case recoveryKeyScreen(selectedRoomID: String?) + + /// Showing the encryption reset flow. + case encryptionResetFlow(selectedRoomID: String?) + /// Showing the start chat screen case startChatScreen(selectedRoomID: String?) @@ -44,6 +50,8 @@ class UserSessionFlowCoordinatorStateMachine { case .roomList(let selectedRoomID), .feedbackScreen(let selectedRoomID), .settingsScreen(let selectedRoomID), + .recoveryKeyScreen(let selectedRoomID), + .encryptionResetFlow(let selectedRoomID), .startChatScreen(let selectedRoomID), .logoutConfirmationScreen(let selectedRoomID), .roomDirectorySearchScreen(let selectedRoomID): @@ -79,12 +87,22 @@ class UserSessionFlowCoordinatorStateMachine { /// The feedback screen has been dismissed case dismissedFeedbackScreen + /// Request presentation of the recovery key screen. + case showRecoveryKeyScreen + /// The recovery key screen has been dismissed. + case dismissedRecoveryKeyScreen + + /// Request presentation of the encryption reset flow. + case startEncryptionResetFlow + /// The encryption reset flow is complete and has been dismissed. + case finishedEncryptionResetFlow + /// Request the start of the start chat flow case showStartChatScreen /// Start chat has been dismissed case dismissedStartChatScreen - /// Logout has been requested and this is the last sesion + /// Logout has been requested and this is the last session case showLogoutConfirmationScreen /// Logout has been cancelled case dismissedLogoutConfirmationScreen @@ -136,6 +154,16 @@ class UserSessionFlowCoordinatorStateMachine { case (.feedbackScreen(let selectedRoomID), .dismissedFeedbackScreen): return .roomList(selectedRoomID: selectedRoomID) + case (.roomList(let selectedRoomID), .showRecoveryKeyScreen): + return .recoveryKeyScreen(selectedRoomID: selectedRoomID) + case (.recoveryKeyScreen(let selectedRoomID), .dismissedRecoveryKeyScreen): + return .roomList(selectedRoomID: selectedRoomID) + + case (.roomList(let selectedRoomID), .startEncryptionResetFlow): + return .encryptionResetFlow(selectedRoomID: selectedRoomID) + case (.encryptionResetFlow(let selectedRoomID), .finishedEncryptionResetFlow): + return .roomList(selectedRoomID: selectedRoomID) + case (.roomList(let selectedRoomID), .showStartChatScreen): return .startChatScreen(selectedRoomID: selectedRoomID) case (.startChatScreen(let selectedRoomID), .dismissedStartChatScreen): diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift index d9b7a2481f..22753bc35c 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift @@ -20,7 +20,8 @@ enum HomeScreenCoordinatorAction { case roomLeft(roomIdentifier: String) case presentSettingsScreen case presentFeedbackScreen - case presentSecureBackupSettings + case presentRecoveryKeyScreen + case presentEncryptionResetScreen case presentStartChatScreen case presentGlobalSearch case presentRoomDirectorySearch @@ -63,8 +64,10 @@ final class HomeScreenCoordinator: CoordinatorProtocol { actionsSubject.send(.presentFeedbackScreen) case .presentSettingsScreen: actionsSubject.send(.presentSettingsScreen) - case .presentSecureBackupSettings: - actionsSubject.send(.presentSecureBackupSettings) + case .presentRecoveryKeyScreen: + actionsSubject.send(.presentRecoveryKeyScreen) + case .presentEncryptionResetScreen: + actionsSubject.send(.presentEncryptionResetScreen) case .presentStartChatScreen: actionsSubject.send(.presentStartChatScreen) case .presentGlobalSearch: diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift index d253a14be5..f0860160cc 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift @@ -13,7 +13,8 @@ enum HomeScreenViewModelAction { case presentRoom(roomIdentifier: String) case presentRoomDetails(roomIdentifier: String) case roomLeft(roomIdentifier: String) - case presentSecureBackupSettings + case presentRecoveryKeyScreen + case presentEncryptionResetScreen case presentSettingsScreen case presentFeedbackScreen case presentStartChatScreen @@ -30,7 +31,8 @@ enum HomeScreenViewAction { case confirmLeaveRoom(roomIdentifier: String) case showSettings case startChat - case confirmRecoveryKey + case manageRecoveryKey + case resetEncryption case skipRecoveryKeyConfirmation case confirmSlidingSyncUpgrade case skipSlidingSyncUpgrade diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index 674559ef80..3c8a241a11 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -138,8 +138,10 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol Task { await leaveRoom(roomID: roomIdentifier) } case .showSettings: actionsSubject.send(.presentSettingsScreen) - case .confirmRecoveryKey: - actionsSubject.send(.presentSecureBackupSettings) + case .manageRecoveryKey: + actionsSubject.send(.presentRecoveryKeyScreen) + case .resetEncryption: + actionsSubject.send(.presentEncryptionResetScreen) case .skipRecoveryKeyConfirmation: state.securityBannerMode = .dismissed case .confirmSlidingSyncUpgrade: diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift index 3215723301..3f4919eee9 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift @@ -56,14 +56,21 @@ struct HomeScreenRecoveryKeyConfirmationBanner: View { var buttons: some View { VStack(spacing: 16) { Button(actionTitle) { - context.send(viewAction: .confirmRecoveryKey) + context.send(viewAction: .manageRecoveryKey) } .frame(maxWidth: .infinity) .buttonStyle(.compound(.primary, size: .medium)) .accessibilityIdentifier(A11yIdentifiers.homeScreen.recoveryKeyConfirmationBannerContinue) if !requiresExtraAccountSetup { - // Missing encryption reset button to goes here once the flow exists. + Button { + context.send(viewAction: .resetEncryption) + } label: { + Text(L10n.confirmRecoveryKeyBannerSecondaryButtonTitle) + .padding(.vertical, 7) + .frame(maxWidth: .infinity) + } + .buttonStyle(.compound(.plain, size: .medium)) } } } diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Out-of-sync.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Out-of-sync.png index d9e29da1ad..02bd87401a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Out-of-sync.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-en-GB.Out-of-sync.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d425040e4de7d03d440c14ad4c1255e9ecd0950e913e340e6828780b008dddbb -size 98068 +oid sha256:1b6c3bbad4b922a61086bca2821a6b2740999bf26df1fd22c979f6e25bd83f49 +size 105130 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Out-of-sync.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Out-of-sync.png index 6ae3571a04..d1e6704b48 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Out-of-sync.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPad-pseudo.Out-of-sync.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:946bbda26c484f64738933c2c4fd3f4b8d095079d38947ef55c17deb4660b1a6 -size 109879 +oid sha256:1e47702cf4ad8b67fe144999a1c8102aed2f3d86bc59588904c7fc11fab09254 +size 118075 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Out-of-sync.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Out-of-sync.png index a0b3aa0e0b..09b95afc40 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Out-of-sync.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-en-GB.Out-of-sync.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:32b70c1911a7a413e0d6ae1748e3ea05929e951503629ead0b2fe3773f0bdd9d -size 56470 +oid sha256:2f3df65735d7447af2345df335ab5350a20fab9e978b6914621f28bbf88104d3 +size 62952 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Out-of-sync.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Out-of-sync.png index 4d2e4c92c1..9dc44dbd3e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Out-of-sync.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_homeScreenRecoveryKeyConfirmationBanner-iPhone-16-pseudo.Out-of-sync.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9e6a4ef76a75e88c0a87e177fd11ec6d1bb9b1b6f843692d002f93b4fa75690 -size 79643 +oid sha256:c16d88503434c47f0d11664dcb9a172339e1638198fbbdb9ae607a91803789ff +size 90729 From 85d497a4d2f9a212a93328a0c74005a2c76b7c20 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:21:54 +0000 Subject: [PATCH 102/114] Stop the sync loop after each background app refresh. (#3481) --- ElementX/Sources/Application/AppCoordinator.swift | 8 ++++++-- ElementX/Sources/Services/Client/ClientProxy.swift | 11 ++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index b46a3aad5d..eec3b77f94 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -988,7 +988,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg // This is important for the app to keep refreshing in the background scheduleBackgroundAppRefresh() - task.expirationHandler = { + task.expirationHandler = { [weak self] in + self?.stopSync() // Attempt to stop the sync loop cleanly. MXLog.info("Background app refresh task expired") task.setTaskCompleted(success: true) } @@ -1007,8 +1008,11 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg .collect(.byTimeOrCount(DispatchQueue.main, .seconds(10), 10)) .sink(receiveValue: { [weak self] _ in guard let self else { return } - MXLog.info("Background app refresh finished") + + // Make sure we stop the sync loop, otherwise the ongoing request is immediately + // handled the next time the app refreshes, which can trigger timeout failures. + stopSync() backgroundRefreshSyncObserver?.cancel() task.setTaskCompleted(success: true) diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 3db90f896b..5d6236a0be 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -306,15 +306,24 @@ class ClientProxy: ClientProxyProtocol { restartTask = nil } + guard let syncService else { + MXLog.warning("No sync service to stop.") + completion?() + return + } + // Capture the sync service strongly as this method is called on deinit and so the // existence of self when the Task executes is questionable and would sometimes crash. + // Note: This isn't strictly necessary now given the unwrap above, but leaving the code as + // documentation. SE-0371 will allow us to fix this by using an async deinit. Task { [syncService] in do { defer { completion?() } - try await syncService?.stop() + try await syncService.stop() + MXLog.info("Sync stopped") } catch { MXLog.error("Failed stopping the sync service with error: \(error)") } From f3a15f793764a8cc4e3d99c1373483a949e878f4 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Tue, 5 Nov 2024 10:52:51 +0000 Subject: [PATCH 103/114] Fix the Setup Recovery flow from the home screen banner. (#3483) --- .../Sources/FlowCoordinators/UserSessionFlowCoordinator.swift | 2 ++ .../Sources/Screens/HomeScreen/HomeScreenCoordinator.swift | 3 +++ ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift | 4 +++- ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift | 4 +++- .../View/HomeScreenRecoveryKeyConfirmationBanner.swift | 3 ++- 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift index 42b6f782f3..b824715d32 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift @@ -466,6 +466,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { settingsFlowCoordinator.handleAppRoute(.settings, animated: true) case .presentFeedbackScreen: stateMachine.processEvent(.feedbackScreen) + case .presentSecureBackupSettings: + settingsFlowCoordinator.handleAppRoute(.chatBackupSettings, animated: true) case .presentRecoveryKeyScreen: stateMachine.processEvent(.showRecoveryKeyScreen) case .presentEncryptionResetScreen: diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift index 22753bc35c..00f2476bee 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift @@ -20,6 +20,7 @@ enum HomeScreenCoordinatorAction { case roomLeft(roomIdentifier: String) case presentSettingsScreen case presentFeedbackScreen + case presentSecureBackupSettings case presentRecoveryKeyScreen case presentEncryptionResetScreen case presentStartChatScreen @@ -64,6 +65,8 @@ final class HomeScreenCoordinator: CoordinatorProtocol { actionsSubject.send(.presentFeedbackScreen) case .presentSettingsScreen: actionsSubject.send(.presentSettingsScreen) + case .presentSecureBackupSettings: + actionsSubject.send(.presentSecureBackupSettings) case .presentRecoveryKeyScreen: actionsSubject.send(.presentRecoveryKeyScreen) case .presentEncryptionResetScreen: diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift index f0860160cc..2e58c17ae7 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift @@ -13,6 +13,7 @@ enum HomeScreenViewModelAction { case presentRoom(roomIdentifier: String) case presentRoomDetails(roomIdentifier: String) case roomLeft(roomIdentifier: String) + case presentSecureBackupSettings case presentRecoveryKeyScreen case presentEncryptionResetScreen case presentSettingsScreen @@ -31,7 +32,8 @@ enum HomeScreenViewAction { case confirmLeaveRoom(roomIdentifier: String) case showSettings case startChat - case manageRecoveryKey + case setupRecovery + case confirmRecoveryKey case resetEncryption case skipRecoveryKeyConfirmation case confirmSlidingSyncUpgrade diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index 3c8a241a11..c1b21cf4f2 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -138,7 +138,9 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol Task { await leaveRoom(roomID: roomIdentifier) } case .showSettings: actionsSubject.send(.presentSettingsScreen) - case .manageRecoveryKey: + case .setupRecovery: + actionsSubject.send(.presentSecureBackupSettings) + case .confirmRecoveryKey: actionsSubject.send(.presentRecoveryKeyScreen) case .resetEncryption: actionsSubject.send(.presentEncryptionResetScreen) diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift index 3f4919eee9..1adf1fac7b 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenRecoveryKeyConfirmationBanner.swift @@ -16,6 +16,7 @@ struct HomeScreenRecoveryKeyConfirmationBanner: View { var title: String { requiresExtraAccountSetup ? L10n.bannerSetUpRecoveryTitle : L10n.confirmRecoveryKeyBannerTitle } var message: String { requiresExtraAccountSetup ? L10n.bannerSetUpRecoveryContent : L10n.confirmRecoveryKeyBannerMessage } var actionTitle: String { requiresExtraAccountSetup ? L10n.bannerSetUpRecoverySubmit : L10n.confirmRecoveryKeyBannerPrimaryButtonTitle } + var primaryAction: HomeScreenViewAction { requiresExtraAccountSetup ? .setupRecovery : .confirmRecoveryKey } var body: some View { VStack(spacing: 16) { @@ -56,7 +57,7 @@ struct HomeScreenRecoveryKeyConfirmationBanner: View { var buttons: some View { VStack(spacing: 16) { Button(actionTitle) { - context.send(viewAction: .manageRecoveryKey) + context.send(viewAction: primaryAction) } .frame(maxWidth: .infinity) .buttonStyle(.compound(.primary, size: .medium)) From c54e4bf6d5166388139012c2368c85e7db4fab9b Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Tue, 5 Nov 2024 18:07:14 +0200 Subject: [PATCH 104/114] Fix race condition when setting up session verification controller subscriptions. --- .../Sources/Services/Client/ClientProxy.swift | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 5d6236a0be..58acb6c317 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -156,19 +156,10 @@ class ClientProxy: ClientProxyProtocol { self?.ignoredUsersSubject.send(ignoredUsers) }) - updateVerificationState(client.encryption().verificationState()) + await updateVerificationState(client.encryption().verificationState()) verificationStateListenerTaskHandle = client.encryption().verificationStateListener(listener: VerificationStateListenerProxy { [weak self] verificationState in - guard let self else { return } - - updateVerificationState(verificationState) - - // The session verification controller requires the user's identity which - // isn't available before a keys query response. Use the verification - // state updates as an aproximation for when that happens. - Task { - await self.buildSessionVerificationControllerProxyIfPossible(verificationState: verificationState) - } + Task { await self?.updateVerificationState(verificationState) } }) sendQueueListenerTaskHandle = client.subscribeToSendQueueStatus(listener: SendQueueRoomErrorListenerProxy { [weak self] roomID, error in @@ -727,7 +718,7 @@ class ClientProxy: ClientProxyProtocol { // MARK: - Private - private func updateVerificationState(_ verificationState: VerificationState) { + private func updateVerificationState(_ verificationState: VerificationState) async { let verificationState: SessionVerificationState = switch verificationState { case .unknown: .unknown @@ -737,10 +728,17 @@ class ClientProxy: ClientProxyProtocol { .verified } + // The session verification controller requires the user's identity which + // isn't available before a keys query response. Use the verification + // state updates as an aproximation for when that happens. + await buildSessionVerificationControllerProxyIfPossible(verificationState: verificationState) + + // Only update the session verification state after creating a session + // verification proxy to avoid race conditions verificationStateSubject.send(verificationState) } - private func buildSessionVerificationControllerProxyIfPossible(verificationState: VerificationState) async { + private func buildSessionVerificationControllerProxyIfPossible(verificationState: SessionVerificationState) async { guard sessionVerificationController == nil, verificationState != .unknown else { return } From 5fc8caca50be476ad5b7d26e0c26d051d938045d Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:48:07 +0000 Subject: [PATCH 105/114] Fix a couple of race conditions when observing room info updates for calls. (#3487) * Fix a race condition observing room info updates for calls. * Fix a bug where call observation wasn't set up if the call comes when the app has been killed. --- .../ElementCall/ElementCallService.swift | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/ElementX/Sources/Services/ElementCall/ElementCallService.swift b/ElementX/Sources/Services/ElementCall/ElementCallService.swift index 9ca9b4a058..95e804d974 100644 --- a/ElementX/Sources/Services/ElementCall/ElementCallService.swift +++ b/ElementX/Sources/Services/ElementCall/ElementCallService.swift @@ -35,14 +35,19 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe return CXProvider(configuration: configuration) }() - private weak var clientProxy: ClientProxyProtocol? + private weak var clientProxy: ClientProxyProtocol? { + didSet { + // There's a race condition where a call starts when the app has been killed and the + // observation set in `incomingCallID` occurs *before* the user session is restored. + // So observe when the client proxy is set to fix this (the method guards for the call). + Task { await observeIncomingCallRoomInfo() } + } + } - private var cancellables = Set() + private var incomingCallRoomInfoCancellable: AnyCancellable? private var incomingCallID: CallID? { didSet { - Task { - await observeIncomingCallRoomStateUpdates() - } + Task { await observeIncomingCallRoomInfo() } } } @@ -260,7 +265,7 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe // MARK: - Private - func tearDownCallSession(sendEndCallAction: Bool = true) { + private func tearDownCallSession(sendEndCallAction: Bool = true) { if sendEndCallAction, let ongoingCallID { let transaction = CXTransaction(action: CXEndCallAction(call: ongoingCallID.callKitID)) callController.request(transaction) { error in @@ -273,30 +278,35 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe ongoingCallID = nil } - func observeIncomingCallRoomStateUpdates() async { - cancellables.removeAll() + private func observeIncomingCallRoomInfo() async { + incomingCallRoomInfoCancellable = nil - guard let clientProxy, let incomingCallID else { + guard let incomingCallID else { + MXLog.info("No incoming call to observe for.") + return + } + + guard let clientProxy else { + MXLog.warning("A ClientProxy is needed to fetch the room.") return } guard case let .joined(roomProxy) = await clientProxy.roomForIdentifier(incomingCallID.roomID) else { + MXLog.warning("Failed to fetch a joined room for the incoming call.") return } roomProxy.subscribeToRoomInfoUpdates() - // There's no incoming event for call cancellations so try to infer - // it from what we have. If the call is running before subscribing then wait - // for it to change to `false` otherwise wait for it to turn `true` before - // changing to `false` - let isCallOngoing = roomProxy.infoPublisher.value.hasRoomCall - - roomProxy + incomingCallRoomInfoCancellable = roomProxy .infoPublisher .compactMap { ($0.hasRoomCall, $0.activeRoomCallParticipants) } .removeDuplicates { $0 == $1 } - .dropFirst(isCallOngoing ? 0 : 1) + .drop(while: { hasRoomCall, _ in + // Filter all updates before hasRoomCall becomes `true`. Then we can correctly + // detect its change to `false` to stop ringing when the caller hangs up. + !hasRoomCall + }) .sink { [weak self] hasOngoingCall, activeRoomCallParticipants in guard let self else { return } @@ -305,17 +315,16 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe if !hasOngoingCall { MXLog.info("Call cancelled by remote") - cancellables.removeAll() + incomingCallRoomInfoCancellable = nil endUnansweredCallTask?.cancel() callProvider.reportCall(with: incomingCallID.callKitID, endedAt: nil, reason: .remoteEnded) } else if participants.contains(roomProxy.ownUserID) { - MXLog.info("Call anwered elsewhere") + MXLog.info("Call answered elsewhere") - cancellables.removeAll() + incomingCallRoomInfoCancellable = nil endUnansweredCallTask?.cancel() callProvider.reportCall(with: incomingCallID.callKitID, endedAt: nil, reason: .answeredElsewhere) } } - .store(in: &cancellables) } } From 8de44b95bf8305efcc80da45b2000048558182d4 Mon Sep 17 00:00:00 2001 From: Velin92 <34335419+Velin92@users.noreply.github.com> Date: Mon, 4 Nov 2024 00:01:52 +0000 Subject: [PATCH 106/114] Translations update --- .../be.lproj/Localizable.strings | 22 +- .../bg.lproj/Localizable.strings | 34 +-- .../cs.lproj/Localizable.strings | 24 +- .../de.lproj/Localizable.strings | 22 +- .../el.lproj/Localizable.strings | 22 +- .../en.lproj/Localizable.strings | 1 + .../es.lproj/Localizable.strings | 22 +- .../et.lproj/Localizable.strings | 28 ++- .../fa.lproj/Localizable.strings | 32 ++- .../fr.lproj/Localizable.strings | 78 +++--- .../hu.lproj/Localizable.strings | 92 +++---- .../id.lproj/Localizable.strings | 164 +++++++------ .../it.lproj/Localizable.strings | 22 +- .../ka.lproj/Localizable.strings | 30 ++- .../nl.lproj/Localizable.strings | 226 +++++++++--------- .../nl.lproj/Localizable.stringsdict | 4 +- .../pl.lproj/Localizable.strings | 118 ++++----- .../pt-BR.lproj/Localizable.strings | 26 +- .../pt.lproj/Localizable.strings | 86 +++---- .../ro.lproj/Localizable.strings | 22 +- .../ru.lproj/Localizable.strings | 32 ++- .../sk.lproj/Localizable.strings | 22 +- .../sv.lproj/Localizable.strings | 22 +- .../uk.lproj/Localizable.strings | 22 +- .../uz.lproj/Localizable.strings | 26 +- .../zh-Hans.lproj/Localizable.strings | 22 +- .../zh-Hant-TW.lproj/Localizable.strings | 38 +-- ElementX/Sources/Generated/Strings.swift | 2 + 28 files changed, 707 insertions(+), 554 deletions(-) diff --git a/ElementX/Resources/Localizations/be.lproj/Localizable.strings b/ElementX/Resources/Localizations/be.lproj/Localizable.strings index b32142aa8e..5e496bb337 100644 --- a/ElementX/Resources/Localizations/be.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/be.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Ваш сервер зараз падтрымлівае новы, хутчэйшы пратакол. Выйдзіце з сістэмы і зноў увайдзіце, каб абнавіць яе. Гэта дапаможа вам пазбегнуць прымусовага выхаду з сістэмы, калі стары пратакол будзе пазней выдалены."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Ваш хатні сервер больш не падтрымлівае стары пратакол. Калі ласка, выйдзіце і ўвайдзіце зноў, каб працягнуць выкарыстанне праграмы."; "banner_migrate_to_native_sliding_sync_title" = "Даступна абнаўленне"; -"banner.set_up_recovery.content" = "Стварыце новы ключ аднаўлення, які можна выкарыстоўваць для аднаўлення зашыфраванай гісторыі паведамленняў у выпадку страты доступу да вашых прылад."; -"banner.set_up_recovery.title" = "Наладзіць аднаўленне"; +"banner_set_up_recovery_content" = "Стварыце новы ключ аднаўлення, які можна выкарыстоўваць для аднаўлення зашыфраванай гісторыі паведамленняў у выпадку страты доступу да вашых прылад."; +"banner_set_up_recovery_title" = "Наладзіць аднаўленне"; "common_about" = "Аб праграме"; "common_acceptable_use_policy" = "Палітыка дапушчальнага выкарыстання"; "common_advanced_settings" = "Пашыраныя налады"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Verification failed"; "common_verified" = "Verified"; "common_verify_device" = "Праверце прыладу"; +"common_verify_identity" = "Verify identity"; "common_video" = "Відэа"; "common_voice_message" = "Галасавое паведамленне"; "common_waiting" = "Чакаем…"; @@ -246,6 +247,8 @@ "common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; "common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Ваша рэзервовая копія чата зараз не сінхранізавана. Вам трэба пацвердзіць ключ аднаўлення, каб захаваць доступ да рэзервовай копіі чата."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "Увядзіце ключ аднаўлення"; "crash_detection_dialog_content" = "Пры апошнім выкарыстанні %1$@ адбыўся збой. Хочаце падзяліцца справаздачай аб збоі?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "Без водступу"; "rich_text_editor_url_placeholder" = "Спасылка"; "rich_text_editor_a11y_add_attachment" = "Дадаць далучэнне"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "screen_advanced_settings_element_call_base_url" = "Карыстальніцкі URL сервера Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Усталюйце карыстальніцкі асноўны URL для Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Адрас пазначаны няправільна, пераканайцеся, што вы ўказалі пратакол (http/https) і правільны адрас."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Вы збіраецеся стварыць уліковы запіс на %@"; "screen_advanced_settings_developer_mode" = "Рэжым распрацоўшчыка"; "screen_advanced_settings_developer_mode_description" = "Падайце распрацоўнікам доступ да функцый і функцыянальным магчымасцям."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "Адключыць рэдактар фарматаванага тэксту і ўключыць Markdown."; "screen_advanced_settings_send_read_receipts" = "Апавяшчэнні аб чытанні"; "screen_advanced_settings_send_read_receipts_description" = "Калі выключыць, вашы пасведчанні аб прачытанні нікому не будуць адпраўляцца. Вы па-ранейшаму будзеце атрымліваць пасведчанні аб прачытанні ад іншых карыстальнікаў."; @@ -458,12 +462,12 @@ "screen_chat_backup_key_backup_action_enable" = "Уключыце рэзервовае капіраванне"; "screen_chat_backup_key_backup_description" = "Рэзервовае капіраванне гарантуе, што вы не страціце сваю гісторыю паведамленняў. %1$@."; "screen_chat_backup_key_backup_title" = "Рэзервовая копія"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; "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" = "Змяніць ключ аднаўлення"; "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" = "Ваша рэзервовая копія чата зараз не сінхранізавана."; -"screen_chat_backup_recovery_action_setup" = "Наладзьце аднаўленне"; "screen_chat_backup_recovery_action_setup_description" = "Атрымайце доступ да зашыфраваных паведамленняў, калі вы страціце ўсе свае прылады або выйдзеце з сістэмы %1$@ усюды."; "screen_create_account_title" = "Стварыць уліковы запіс"; "screen_create_new_recovery_key_list_item_1" = "Адкрыйце %1$@ на настольнай прыладзе"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Паказаць менш"; "screen_room_timeline_message_copied" = "Паведамленне скапіравана"; "screen_room_timeline_no_permission_to_post" = "У Вас няма дазволу на публікацыю ў гэтым пакоі"; -"screen_room_timeline_reactions_show_less" = "Паказаць менш"; "screen_room_timeline_reactions_show_more" = "Паказаць больш"; "screen_room_timeline_read_marker_title" = "Новае"; "screen_room_title" = "Чат"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "Параўнайце ўнікальны набор эмодзі."; "screen_session_verification_request_accepted_subtitle" = "Параўнайце ўнікальныя эмодзі, пераканаўшыся, што яны размешчаны ў тым жа парадку."; "screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "Размеркавальнікі не знойдзены."; "troubleshoot_notifications_test_unified_push_title" = "Праверыць UnifiedPush"; "a11y_poll" = "Апытанне"; +"banner_set_up_recovery_submit" = "Наладзьце аднаўленне"; "dialog_title_error" = "Памылка"; "dialog_title_success" = "Поспех"; "notification_fallback_content" = "Апавяшчэнне"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Разблакіраваць карыстальніка"; "screen_bug_report_rash_logs_alert_title" = "Пры апошнім выкарыстанні %1$@ адбыўся збой. Хочаце падзяліцца справаздачай аб збоі?"; "screen_chat_backup_recovery_action_confirm" = "Увядзіце ключ аднаўлення"; +"screen_chat_backup_recovery_action_setup" = "Наладзьце аднаўленне"; "screen_create_poll_cancel_confirmation_content_ios" = "Вашы змены не будуць захаваны"; "screen_create_room_add_people_title" = "Запрасіць карыстальнікаў"; "screen_create_room_room_name_label" = "Назва пакоя"; @@ -1029,7 +1033,7 @@ "screen_notification_settings_mentions_section_title" = "Згадванні"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Паўтарыць спробу"; "screen_recovery_key_change_generate_key_description" = "Пераканайцеся, што вы можаце захаваць ключ аднаўлення ў бяспечным месцы"; -"screen_recovery_key_confirm_title" = "Увядзіце ключ аднаўлення"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Заблакіраваць карыстальніка"; "screen_reset_encryption_password_placeholder" = "Увесці..."; "screen_room_attachment_source_camera_photo" = "Зрабіць фота"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Не атрымалася апрацаваць медыяфайл для загрузкі, паспрабуйце яшчэ раз."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Выдаліць і заблакіраваць удзельніка"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Толькі згадванні і ключавыя словы"; +"screen_room_timeline_reactions_show_less" = "Паказаць менш"; "screen_roomlist_filter_people" = "Людзі"; "screen_server_confirmation_change_server" = "Змяніць правайдара ўліковага запісу"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "Выйсці"; "screen_signout_confirmation_dialog_title" = "Выйсці"; "screen_signout_key_backup_offline_title" = "Вашы ключы ўсё яшчэ ствараюцца"; diff --git a/ElementX/Resources/Localizations/bg.lproj/Localizable.strings b/ElementX/Resources/Localizations/bg.lproj/Localizable.strings index ea1cf7fad3..8e7a783ffe 100644 --- a/ElementX/Resources/Localizations/bg.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/bg.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; "banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; -"banner.set_up_recovery.title" = "Set up recovery"; +"banner_set_up_recovery_content" = "Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices."; +"banner_set_up_recovery_title" = "Set up recovery to protect your account"; "common_about" = "Относно"; "common_acceptable_use_policy" = "Acceptable use policy"; "common_advanced_settings" = "Разширени настройки"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Verification failed"; "common_verified" = "Verified"; "common_verify_device" = "Потвърждаване на устройството"; +"common_verify_identity" = "Verify identity"; "common_video" = "Видео"; "common_voice_message" = "Гласово съобщение"; "common_waiting" = "Waiting…"; @@ -246,6 +247,8 @@ "common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; "common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Резервното копие на чатовете ви в момента не е синхронизирано. Въведете ключа си за възстановяване, за да потвърдите достъпа до резервното копие на чатовете си."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "Потвърдете ключа си за възстановяване"; "crash_detection_dialog_content" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "Unindent"; "rich_text_editor_url_placeholder" = "Връзка"; "rich_text_editor_a11y_add_attachment" = "Прикачване на файл"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "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."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "На път сте да създадете акаунт в %@"; "screen_advanced_settings_developer_mode" = "Developer mode"; "screen_advanced_settings_developer_mode_description" = "Enable to have access to features and functionality for developers."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "Disable the rich text editor to type Markdown manually."; "screen_advanced_settings_send_read_receipts" = "Потвърждения за прочитане"; "screen_advanced_settings_send_read_receipts_description" = "If turned off, your read receipts won't be sent to anyone. You will still receive read receipts from other users."; @@ -458,12 +462,12 @@ "screen_chat_backup_key_backup_action_enable" = "Включване на резервните копия"; "screen_chat_backup_key_backup_description" = "Резервното копие гарантира, че няма да загубите хронологията на съобщенията си. %1$@."; "screen_chat_backup_key_backup_title" = "Резервно копие"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; "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" = "Промяна на ключа за възстановяване"; "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" = "Резервното копие на чатовете ви в момента не е синхронизирано."; -"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."; "screen_create_account_title" = "Create account"; "screen_create_new_recovery_key_list_item_1" = "Open %1$@ in a desktop device"; @@ -538,10 +542,10 @@ "screen_key_backup_disable_confirmation_action_turn_off" = "Изключване"; "screen_key_backup_disable_confirmation_description" = "You will lose your encrypted messages if you are signed out of all devices."; "screen_key_backup_disable_confirmation_title" = "Are you sure you want to turn off backup?"; -"screen_key_backup_disable_description" = "Turning off backup will remove your current encryption key backup and turn off other security features. In this case, you will:"; -"screen_key_backup_disable_description_point_1" = "Not have encrypted message history on new devices"; -"screen_key_backup_disable_description_point_2" = "Lose access to your encrypted messages if you are signed out of %1$@ everywhere"; -"screen_key_backup_disable_title" = "Are you sure you want to turn off backup?"; +"screen_key_backup_disable_description" = "Deleting key storage will remove your cryptographic identity and message keys from the server and turn off the following security features:"; +"screen_key_backup_disable_description_point_1" = "You will not have encrypted message history on new devices"; +"screen_key_backup_disable_description_point_2" = "You will lose access to your encrypted messages if you are signed out of %1$@ everywhere"; +"screen_key_backup_disable_title" = "Are you sure you want to turn off key storage and delete it?"; "screen_login_error_deactivated_account" = "Този акаунт бе деактивиран."; "screen_login_error_invalid_credentials" = "Неправилно потребителско име и/или парола"; "screen_login_error_invalid_user_id" = "This is not a valid user identifier. Expected format: ‘@user:homeserver.org’"; @@ -638,7 +642,7 @@ "screen_recovery_key_change_title" = "Промяна на ключа за възстановяване?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Create new recovery key"; "screen_recovery_key_confirm_description" = "Уверете се, че никой не може да види този екран!"; -"screen_recovery_key_confirm_error_content" = "Please try again to confirm access to your chat backup."; +"screen_recovery_key_confirm_error_content" = "Please try again to confirm access to your key storage."; "screen_recovery_key_confirm_error_title" = "Неправилен ключ за възстановяване"; "screen_recovery_key_confirm_key_description" = "Въведете 48-символния код."; "screen_recovery_key_confirm_key_placeholder" = "Въведете…"; @@ -652,7 +656,7 @@ "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_description" = "Your key storage is protected by a recovery key. If you need a new recovery key after setup, you can recreate it by selecting ‘Change recovery key’."; "screen_recovery_key_setup_generate_key" = "Generate your recovery key"; "screen_recovery_key_setup_generate_key_description" = "Do not share this with anyone!"; "screen_recovery_key_setup_success" = "Recovery setup successful"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Показване на по-малко"; "screen_room_timeline_message_copied" = "Съобщението е копирано"; "screen_room_timeline_no_permission_to_post" = "You do not have permission to post to this room"; -"screen_room_timeline_reactions_show_less" = "Показване на по-малко"; "screen_room_timeline_reactions_show_more" = "Показване на повече"; "screen_room_timeline_read_marker_title" = "New"; "screen_room_title" = "Chat"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "Сравнете уникален набор от емоджита."; "screen_session_verification_request_accepted_subtitle" = "Compare the unique emoji, ensuring they appear in the same order."; "screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "No push distributors found."; "troubleshoot_notifications_test_unified_push_title" = "Check UnifiedPush"; "a11y_poll" = "Анкета"; +"banner_set_up_recovery_submit" = "Set up recovery"; "dialog_title_error" = "Грешка"; "dialog_title_success" = "Успешно"; "notification_fallback_content" = "Известие"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Отблокиране на потребителя"; "screen_bug_report_rash_logs_alert_title" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; "screen_chat_backup_recovery_action_confirm" = "Въвеждане на ключ за възстановяване"; +"screen_chat_backup_recovery_action_setup" = "Set up recovery"; "screen_create_poll_cancel_confirmation_content_ios" = "Your changes won’t be saved"; "screen_create_room_add_people_title" = "Поканване на хора"; "screen_create_room_room_name_label" = "Име на стаята"; @@ -1029,7 +1033,7 @@ "screen_notification_settings_mentions_section_title" = "Споменавания"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Повторен опит"; "screen_recovery_key_change_generate_key_description" = "Do not share this with anyone!"; -"screen_recovery_key_confirm_title" = "Потвърдете ключа си за възстановяване"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Блокиране на потребителя"; "screen_reset_encryption_password_placeholder" = "Въведете…"; "screen_room_attachment_source_camera_photo" = "Снимка"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Failed processing media to upload, please try again."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Remove and ban member"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Само споменавания и ключови думи"; +"screen_room_timeline_reactions_show_less" = "Показване на по-малко"; "screen_roomlist_filter_people" = "Хора"; "screen_server_confirmation_change_server" = "Промяна на доставчика на акаунт"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "Изход"; "screen_signout_confirmation_dialog_title" = "Изход"; "screen_signout_key_backup_offline_title" = "Your keys are still being backed up"; diff --git a/ElementX/Resources/Localizations/cs.lproj/Localizable.strings b/ElementX/Resources/Localizations/cs.lproj/Localizable.strings index b05c85c3f3..bc72b44767 100644 --- a/ElementX/Resources/Localizations/cs.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/cs.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Váš server nyní podporuje nový, rychlejší protokol. Chcete-li upgradovat, odhlaste se a znovu se přihlaste. Pokud to uděláte nyní, pomůže vám vyhnout se nucenému odhlášení, když bude starý protokol později odstraněn."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Váš domovský server již nepodporuje starý protokol. Chcete-li pokračovat v používání aplikace, odhlaste se a znovu se přihlaste."; "banner_migrate_to_native_sliding_sync_title" = "Upgrade k dispozici"; -"banner.set_up_recovery.content" = "Vygenerujte nový klíč pro obnovení, který lze použít k obnovení historie šifrovaných zpráv v případě, že ztratíte přístup ke svým zařízením."; -"banner.set_up_recovery.title" = "Nastavení obnovy"; +"banner_set_up_recovery_content" = "Vygenerujte nový klíč pro obnovení, který lze použít k obnovení historie šifrovaných zpráv v případě, že ztratíte přístup ke svým zařízením."; +"banner_set_up_recovery_title" = "Nastavení obnovy"; "common_about" = "O aplikaci"; "common_acceptable_use_policy" = "Zásady používání"; "common_advanced_settings" = "Pokročilá nastavení"; @@ -150,7 +150,7 @@ "common_favourited" = "Oblíbené"; "common_file" = "Soubor"; "common_forward_message" = "Přeposlat zprávu"; -"common_frequently_used" = "Frequently used"; +"common_frequently_used" = "Často používané"; "common_gif" = "GIF"; "common_image" = "Obrázek"; "common_in_reply_to" = "V odpovědi na %1$@"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Ověření se nezdařilo"; "common_verified" = "Ověřeno"; "common_verify_device" = "Ověřit zařízení"; +"common_verify_identity" = "Ověření totožnosti"; "common_video" = "Video"; "common_voice_message" = "Hlasová zpráva"; "common_waiting" = "Čekání..."; @@ -246,6 +247,8 @@ "common_unable_to_decrypt_insecure_device" = "Šifrováno nezabezpečeným zařízením"; "common_unable_to_decrypt_verification_violation" = "Ověřená identita odesílatele se změnila"; "confirm_recovery_key_banner_message" = "Vaše záloha chatu není aktuálně synchronizována. Abyste si zachovali přístup k záloze chatu, musíte potvrdit klíč pro obnovení."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "Potvrďte klíč pro obnovení"; "crash_detection_dialog_content" = "%1$@ havaroval při posledním použití. Chcete se s námi podělit o zprávu o selhání?"; "crypto_identity_change_pin_violation" = "Zdá se, že se identita %1$@ změnila. %2$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "Zrušit odsazení"; "rich_text_editor_url_placeholder" = "Odkaz"; "rich_text_editor_a11y_add_attachment" = "Přidat přílohu"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "screen_advanced_settings_element_call_base_url" = "Vlastní URL pro Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Nastavte vlastní URL pro Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Neplatné URL, ujistěte se, že jste uvedli protokol (http/https) a správnou adresu."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Chystáte se vytvořit účet na %@"; "screen_advanced_settings_developer_mode" = "Vývojářský režim"; "screen_advanced_settings_developer_mode_description" = "Povolením získáte přístup k funkcím a funkcím pro vývojáře."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Optimalizovat pro nahrávání"; +"screen_advanced_settings_media_compression_title" = "Média"; "screen_advanced_settings_rich_text_editor_description" = "Vypněte editor formátovaného textu pro ruční zadání Markdown."; "screen_advanced_settings_send_read_receipts" = "Potvrzení o přečtení"; "screen_advanced_settings_send_read_receipts_description" = "Pokud je vypnuto, potvrzení o přečtení se nikomu neodesílají. Stále budete dostávat potvrzení o přečtení od ostatních uživatelů."; @@ -458,12 +462,12 @@ "screen_chat_backup_key_backup_action_enable" = "Zapnout zálohování"; "screen_chat_backup_key_backup_description" = "Bezpečně uložte svou kryptografickou identitu a klíče zpráv na serveru. To vám umožní zobrazit historii zpráv na všech nových zařízeních. %1$@."; "screen_chat_backup_key_backup_title" = "Úložiště klíčů"; +"screen_chat_backup_key_storage_disabled_error" = "Pro nastavení obnovení musí být zapnuto úložiště klíčů."; "screen_chat_backup_key_storage_toggle_description" = "Nahrát klíče z tohoto zařízení"; "screen_chat_backup_key_storage_toggle_title" = "Povolit ukládání klíčů"; "screen_chat_backup_recovery_action_change" = "Změnit klíč pro obnovení"; "screen_chat_backup_recovery_action_change_description" = "Obnovte svou kryptografickou identitu a historii zpráv pomocí klíče pro obnovení, pokud jste ztratili všechna stávající zařízení."; "screen_chat_backup_recovery_action_confirm_description" = "Vaše záloha chatu není aktuálně synchronizována."; -"screen_chat_backup_recovery_action_setup" = "Nastavení obnovy"; "screen_chat_backup_recovery_action_setup_description" = "Získejte přístup ke svým zašifrovaným zprávám, pokud ztratíte všechna zařízení nebo jste všude odhlášeni z %1$@."; "screen_create_account_title" = "Vytvořit účet"; "screen_create_new_recovery_key_list_item_1" = "Otevřít %1$@ na stolním počítači"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Zobrazit méně"; "screen_room_timeline_message_copied" = "Zpráva zkopírována"; "screen_room_timeline_no_permission_to_post" = "Nemáte oprávnění zveřejňovat příspěvky v této místnosti"; -"screen_room_timeline_reactions_show_less" = "Zobrazit méně"; "screen_room_timeline_reactions_show_more" = "Zobrazit více"; "screen_room_timeline_read_marker_title" = "Nové"; "screen_room_title" = "Chat"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "Porovnejte jedinečnou sadu emotikonů."; "screen_session_verification_request_accepted_subtitle" = "Porovnejte jedinečné emotikony a ujistěte se, že jsou zobrazeny ve stejném pořadí."; "screen_session_verification_request_details_timestamp" = "Přihlášen"; -"screen_session_verification_request_failure_subtitle" = "Buď vypršel časový limit požadavku, požadavek byl zamítnut, nebo došlo k nesouladu ověření."; "screen_session_verification_request_failure_title" = "Ověření se nezdařilo"; "screen_session_verification_request_footer" = "Pokračujte, pouze pokud jste toto ověření zahájili."; "screen_session_verification_request_subtitle" = "Ověřte druhé zařízení, aby byla vaše historie zpráv zabezpečená."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "Nebyli nalezeni žádní push distributoři."; "troubleshoot_notifications_test_unified_push_title" = "Zkontrolovat UnifiedPush"; "a11y_poll" = "Hlasování"; +"banner_set_up_recovery_submit" = "Nastavení obnovy"; "dialog_title_error" = "Chyba"; "dialog_title_success" = "Úspěch"; "notification_fallback_content" = "Oznámení"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Odblokovat uživatele"; "screen_bug_report_rash_logs_alert_title" = "%1$@ havaroval při posledním použití. Chcete se s námi podělit o zprávu o selhání?"; "screen_chat_backup_recovery_action_confirm" = "Zadejte klíč pro obnovení"; +"screen_chat_backup_recovery_action_setup" = "Nastavení obnovy"; "screen_create_poll_cancel_confirmation_content_ios" = "Vaše změny nebudou uloženy"; "screen_create_room_add_people_title" = "Pozvat přátele"; "screen_create_room_room_name_label" = "Název místnosti"; @@ -1029,7 +1033,7 @@ "screen_notification_settings_mentions_section_title" = "Zmínky"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Zkusit znovu"; "screen_recovery_key_change_generate_key_description" = "Toto s nikým nesdílejte!"; -"screen_recovery_key_confirm_title" = "Potvrďte klíč pro obnovení"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Zablokovat uživatele"; "screen_reset_encryption_password_placeholder" = "Zadejte..."; "screen_room_attachment_source_camera_photo" = "Vyfotit"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Nahrání média se nezdařilo, zkuste to prosím znovu."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Odebrat a vykázat člena"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Pouze zmínky a klíčová slova"; +"screen_room_timeline_reactions_show_less" = "Zobrazit méně"; "screen_roomlist_filter_people" = "Lidé"; "screen_server_confirmation_change_server" = "Změnit poskytovatele účtu"; +"screen_session_verification_request_failure_subtitle" = "Buď vypršel časový limit požadavku, požadavek byl zamítnut, nebo došlo k nesouladu ověření."; "screen_signout_confirmation_dialog_submit" = "Odhlásit se"; "screen_signout_confirmation_dialog_title" = "Odhlásit se"; "screen_signout_key_backup_offline_title" = "Vaše klíče jsou stále zálohovány"; diff --git a/ElementX/Resources/Localizations/de.lproj/Localizable.strings b/ElementX/Resources/Localizations/de.lproj/Localizable.strings index fad71f88a8..0ba57e7b95 100644 --- a/ElementX/Resources/Localizations/de.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/de.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Dein Server unterstützt jetzt ein neues, schnelleres Protokoll. Melde dich ab und melde dich wieder an, um zu aktualisieren. Wenn du das jetzt tust, vermeidest du eine erzwungene Abmeldung, wenn das alte Protokoll später entfernt wird."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Dein Homeserver unterstützt das alte Protokoll nicht mehr. Bitte logge dich aus und melde dich wieder an, um die App weiter zu nutzen."; "banner_migrate_to_native_sliding_sync_title" = "Aktualisierung verfügbar"; -"banner.set_up_recovery.content" = "Erstelle einen neuen Wiederherstellungsschlüssel, mit dem du deinen verschlüsselten Nachrichtenverlauf wiederherstellen kannst, wenn du dich an einem neuen Gerät anmeldest."; -"banner.set_up_recovery.title" = "Wiederherstellung einrichten"; +"banner_set_up_recovery_content" = "Erstelle einen neuen Wiederherstellungsschlüssel, mit dem du deinen verschlüsselten Nachrichtenverlauf wiederherstellen kannst, wenn du dich an einem neuen Gerät anmeldest."; +"banner_set_up_recovery_title" = "Wiederherstellung einrichten"; "common_about" = "Über"; "common_acceptable_use_policy" = "Nutzungsrichtlinie"; "common_advanced_settings" = "Erweiterte Einstellungen"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Verification failed"; "common_verified" = "Verified"; "common_verify_device" = "Gerät verifizieren"; +"common_verify_identity" = "Verify identity"; "common_video" = "Video"; "common_voice_message" = "Sprachnachricht"; "common_waiting" = "Warten…"; @@ -246,6 +247,8 @@ "common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; "common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Dein Chat-Backup ist derzeit nicht synchronisiert. Du musst deinen Wiederherstellungsschlüssel bestätigen, um Zugriff auf dein Chat-Backup zu erhalten."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "Wiederherstellungsschlüssel bestätigen."; "crash_detection_dialog_content" = "%1$@ ist bei der letzten Nutzung abgestürzt. Möchtest du einen Absturzbericht mit uns teilen?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "Ohne Einrückung"; "rich_text_editor_url_placeholder" = "Link"; "rich_text_editor_a11y_add_attachment" = "Anhang hinzufügen"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "screen_advanced_settings_element_call_base_url" = "Benutzerdefinierte Element-Aufruf-Basis-URL"; "screen_advanced_settings_element_call_base_url_description" = "Lege eine eigene Basis-URL für Element Call fest."; "screen_advanced_settings_element_call_base_url_validation_error" = "Ungültige URL, bitte stelle sicher, dass du das Protokoll (http/https) und die richtige Adresse angibst."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Du bist dabei, ein Konto bei %@ zu erstellen"; "screen_advanced_settings_developer_mode" = "Entwickler-Modus"; "screen_advanced_settings_developer_mode_description" = "Aktivieren, um Zugriff auf Features und Funktionen für Entwickler zu aktivieren."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "Deaktiviere den Rich-Text-Editor, um Markdown manuell einzugeben."; "screen_advanced_settings_send_read_receipts" = "Lesebestätigungen"; "screen_advanced_settings_send_read_receipts_description" = "Wenn diese Option deaktiviert ist, werden Ihre Lesebestätigungen an niemanden gesendet. Du erhältst weiterhin Lesebestätigungen von anderen Benutzern."; @@ -458,12 +462,12 @@ "screen_chat_backup_key_backup_action_enable" = "Backup aktivieren"; "screen_chat_backup_key_backup_description" = "Das Backup stellt sicher, dass du deinen Nachrichtenverlauf nicht verlierst. %1$@."; "screen_chat_backup_key_backup_title" = "Backup"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; "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" = "Wiederherstellungsschlüssel ändern"; "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" = "Dein Chat-Backup ist derzeit nicht synchronisiert."; -"screen_chat_backup_recovery_action_setup" = "Wiederherstellung einrichten"; "screen_chat_backup_recovery_action_setup_description" = "Erhalte Zugriff auf deine verschlüsselten Nachrichten, wenn du alle deine Geräte verlierst oder von %1$@ überall abgemeldet bist."; "screen_create_account_title" = "Konto erstellen"; "screen_create_new_recovery_key_list_item_1" = "Öffne %1$@ auf einem Desktop-Gerät"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Weniger anzeigen"; "screen_room_timeline_message_copied" = "Nachricht wurde kopiert"; "screen_room_timeline_no_permission_to_post" = "Du bist nicht berechtigt, in diesem Raum zu schreiben"; -"screen_room_timeline_reactions_show_less" = "Weniger anzeigen"; "screen_room_timeline_reactions_show_more" = "Mehr anzeigen"; "screen_room_timeline_read_marker_title" = "Neu"; "screen_room_title" = "Chat"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "Vergleiche eine spezielle Reihe von Emojis."; "screen_session_verification_request_accepted_subtitle" = "Vergleiche die einzelnen Emojis und stelle sicher, dass sie in der gleichen Reihenfolge erscheinen."; "screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "Keine Push-Verteiler gefunden."; "troubleshoot_notifications_test_unified_push_title" = "UnifiedPush prüfen"; "a11y_poll" = "Umfrage"; +"banner_set_up_recovery_submit" = "Wiederherstellung einrichten"; "dialog_title_error" = "Fehler"; "dialog_title_success" = "Erfolg"; "notification_fallback_content" = "Mitteilung"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Blockierung aufheben"; "screen_bug_report_rash_logs_alert_title" = "%1$@ ist bei der letzten Nutzung abgestürzt. Möchtest du einen Absturzbericht mit uns teilen?"; "screen_chat_backup_recovery_action_confirm" = "Wiederherstellungsschlüssel eingeben"; +"screen_chat_backup_recovery_action_setup" = "Wiederherstellung einrichten"; "screen_create_poll_cancel_confirmation_content_ios" = "Deine Änderungen werden nicht gespeichert"; "screen_create_room_add_people_title" = "Personen einladen"; "screen_create_room_room_name_label" = "Raumname"; @@ -1029,7 +1033,7 @@ "screen_notification_settings_mentions_section_title" = "Erwähnungen"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Erneut versuchen"; "screen_recovery_key_change_generate_key_description" = "Stelle sicher, dass du deinen Wiederherstellungsschlüssel an einem sicheren Ort aufbewahren kannst"; -"screen_recovery_key_confirm_title" = "Wiederherstellungsschlüssel bestätigen."; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Benutzer blockieren"; "screen_reset_encryption_password_placeholder" = "Eingeben..."; "screen_room_attachment_source_camera_photo" = "Foto aufnehmen"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Fehler beim Verarbeiten des hochgeladenen Mediums. Bitte versuche es erneut."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Mitglied entfernen und sperren"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Nur Erwähnungen und Schlüsselwörter"; +"screen_room_timeline_reactions_show_less" = "Weniger anzeigen"; "screen_roomlist_filter_people" = "Personen"; "screen_server_confirmation_change_server" = "Kontoanbieter wechseln"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "Abmelden"; "screen_signout_confirmation_dialog_title" = "Abmelden"; "screen_signout_key_backup_offline_title" = "Deine Schlüssel werden noch gesichert"; diff --git a/ElementX/Resources/Localizations/el.lproj/Localizable.strings b/ElementX/Resources/Localizations/el.lproj/Localizable.strings index f608c1385f..9c73cfbee0 100644 --- a/ElementX/Resources/Localizations/el.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/el.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Ο διακομιστής σου υποστηρίζει τώρα ένα νέο, ταχύτερο πρωτόκολλο. Αποσυνδέσου και συνδέσου ξανά για αναβάθμιση τώρα. Κάνοντας αυτό τώρα θα σε βοηθήσει να αποφύγεις μια αναγκαστική αποσύνδεση όταν το παλιό πρωτόκολλο καταργηθεί αργότερα."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Ο οικιακός διακομιστής σου δεν υποστηρίζει πλέον το παλιό πρωτόκολλο. Αποσυνδέσου και συνδέσου ξανά για να συνεχίσεις να χρησιμοποιείς την εφαρμογή."; "banner_migrate_to_native_sliding_sync_title" = "Διαθέσιμη αναβάθμιση"; -"banner.set_up_recovery.content" = "Δημιούργησε ένα νέο κλειδί ανάκτησης που μπορεί να χρησιμοποιηθεί για την επαναφορά του ιστορικού των κρυπτογραφημένων μηνυμάτων σου σε περίπτωση που χάσεις την πρόσβαση στις συσκευές σου."; -"banner.set_up_recovery.title" = "Ρύθμιση ανάκτησης"; +"banner_set_up_recovery_content" = "Δημιούργησε ένα νέο κλειδί ανάκτησης που μπορεί να χρησιμοποιηθεί για την επαναφορά του ιστορικού των κρυπτογραφημένων μηνυμάτων σου σε περίπτωση που χάσεις την πρόσβαση στις συσκευές σου."; +"banner_set_up_recovery_title" = "Ρύθμιση ανάκτησης"; "common_about" = "Σχετικά"; "common_acceptable_use_policy" = "Πολιτική αποδεκτής χρήσης"; "common_advanced_settings" = "Ρυθμίσεις για προχωρημένους"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Verification failed"; "common_verified" = "Επαληθεύτηκε"; "common_verify_device" = "Επαλήθευση συσκευής"; +"common_verify_identity" = "Verify identity"; "common_video" = "Βίντεο"; "common_voice_message" = "Φωνητικό μήνυμα"; "common_waiting" = "Αναμονή…"; @@ -246,6 +247,8 @@ "common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; "common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Το αντίγραφο ασφαλείας της συνομιλίας σου δεν είναι συγχρονισμένο αυτήν τη στιγμή. Πρέπει να εισαγάγεις το κλειδί ανάκτησης για να διατηρήσεις την πρόσβαση στο αντίγραφο ασφαλείας της συνομιλίας σου."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "Εισήγαγε το κλειδί ανάκτησης"; "crash_detection_dialog_content" = "Το %1$@ διακόπηκε την τελευταία φορά που χρησιμοποιήθηκε. Θα 'θελες να μοιραστείς μια αναφορά σφάλματος μαζί μας;"; "crypto_identity_change_pin_violation" = "Η ταυτότητα του χρήστη %1$@ φαίνεται να έχει αλλάξει. %2$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "Χωρίς εσοχή"; "rich_text_editor_url_placeholder" = "Σύνδεσμος"; "rich_text_editor_a11y_add_attachment" = "Προσθήκη συνημμένου"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "screen_advanced_settings_element_call_base_url" = "Προσαρμοσμένο URL βάσης κλήσεων Element"; "screen_advanced_settings_element_call_base_url_description" = "Όρισε μια προσαρμοσμένη διεύθυνση βάσης URL για κλήση Element."; "screen_advanced_settings_element_call_base_url_validation_error" = "Μη έγκυρη διεύθυνση URL, βεβαιώσου ότι έχεις συμπεριλάβει το πρωτόκολλο (http/https) και τη σωστή διεύθυνση."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Πρόκειται να δημιουργήσεις έναν λογαριασμό στο %@"; "screen_advanced_settings_developer_mode" = "Λειτουργία προγραμματιστή"; "screen_advanced_settings_developer_mode_description" = "Ενεργοποίησε την πρόσβαση σε δυνατότητες και λειτουργικότητα για προγραμματιστές."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "Απενεργοποίησε τον επεξεργαστή εμπλουτισμένου κειμένου για να πληκτρολογήσεις Markdown χειροκίνητα."; "screen_advanced_settings_send_read_receipts" = "Αποδεικτικά ανάγνωσης"; "screen_advanced_settings_send_read_receipts_description" = "Εάν απενεργοποιηθεί, τα αποδεικτικά ανάγνωσης δεν θα στέλνονται σε κανέναν. Θα εξακολουθείς να λαμβάνεις αποδεικτικά ανάγνωσης από άλλους χρήστες."; @@ -458,12 +462,12 @@ "screen_chat_backup_key_backup_action_enable" = "Ενεργοποίηση αντιγράφων ασφαλείας"; "screen_chat_backup_key_backup_description" = "Αποθήκευσε την κρυπτογραφική σου ταυτότητα και τα κλειδιά μηνυμάτων με ασφάλεια στον διακομιστή. Αυτό θα σου επιτρέψει να δεις το ιστορικό μηνυμάτων σου σε οποιεσδήποτε νέες συσκευές. %1$@."; "screen_chat_backup_key_backup_title" = "Χώρος αποθήκευσης κλειδιού"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; "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" = "Αλλαγή κλειδιού ανάκτησης"; "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" = "Το αντίγραφο ασφαλείας της συνομιλίας σου δεν είναι συγχρονισμένο αυτήν τη στιγμή."; -"screen_chat_backup_recovery_action_setup" = "Ρύθμιση ανάκτησης"; "screen_chat_backup_recovery_action_setup_description" = "Απόκτησε πρόσβαση στα κρυπτογραφημένα σου μηνύματα εάν χάσεις όλες τις συσκευές σου ή έχεις αποσυνδεθεί από το %1$@ παντού."; "screen_create_account_title" = "Δημιουργία λογαριασμού"; "screen_create_new_recovery_key_list_item_1" = "Άνοιγμα %1$@ σε συσκευή υπολογιστή"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Εμφάνιση λιγότερων"; "screen_room_timeline_message_copied" = "Το μήνυμα αντιγράφηκε"; "screen_room_timeline_no_permission_to_post" = "Δεν έχεις άδεια να δημοσιεύσεις σε αυτό το δωμάτιο"; -"screen_room_timeline_reactions_show_less" = "Εμφάνιση λιγότερων"; "screen_room_timeline_reactions_show_more" = "Εμφάνιση περισσότερων"; "screen_room_timeline_read_marker_title" = "Νέο"; "screen_room_title" = "Συνομιλία"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "Σύγκρινε ένα μοναδικό σύνολο emojis."; "screen_session_verification_request_accepted_subtitle" = "Σύγκρινε τα μοναδικά emoji και σιγουρέψου ότι εμφανίζονται με την ίδια σειρά."; "screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "Δεν βρέθηκαν διανομείς push."; "troubleshoot_notifications_test_unified_push_title" = "Έλεγχος UnifiedPush"; "a11y_poll" = "Δημοσκόπηση"; +"banner_set_up_recovery_submit" = "Ρύθμιση ανάκτησης"; "dialog_title_error" = "Σφάλμα"; "dialog_title_success" = "Επιτυχία"; "notification_fallback_content" = "Γνωστοποίηση"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Κατάργηση αποκλεισμού χρήστη"; "screen_bug_report_rash_logs_alert_title" = "Το %1$@ διακόπηκε την τελευταία φορά που χρησιμοποιήθηκε. Θα 'θελες να μοιραστείς μια αναφορά σφάλματος μαζί μας;"; "screen_chat_backup_recovery_action_confirm" = "Εισαγωγή κλειδιού ανάκτησης"; +"screen_chat_backup_recovery_action_setup" = "Ρύθμιση ανάκτησης"; "screen_create_poll_cancel_confirmation_content_ios" = "Οι αλλαγές σου δεν θα αποθηκευτούν"; "screen_create_room_add_people_title" = "Πρόσκληση ατόμων"; "screen_create_room_room_name_label" = "Όνομα δωματίου"; @@ -1029,7 +1033,7 @@ "screen_notification_settings_mentions_section_title" = "Αναφορές"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Προσπάθησε ξανά"; "screen_recovery_key_change_generate_key_description" = "Μην το μοιραστείς με κανέναν!"; -"screen_recovery_key_confirm_title" = "Εισήγαγε το κλειδί ανάκτησης"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Αποκλεισμός χρήστη"; "screen_reset_encryption_password_placeholder" = "Εισαγωγή..."; "screen_room_attachment_source_camera_photo" = "Τράβηξε φωτογραφία"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Αποτυχία μεταφόρτωσης μέσου, δοκίμασε ξανά."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Αφαίρεση και αποκλεισμός μέλους"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Μόνο αναφορές και λέξεις-κλειδιά"; +"screen_room_timeline_reactions_show_less" = "Εμφάνιση λιγότερων"; "screen_roomlist_filter_people" = "Άτομα"; "screen_server_confirmation_change_server" = "Αλλαγή παρόχου λογαριασμού"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "Αποσύνδεση"; "screen_signout_confirmation_dialog_title" = "Αποσύνδεση"; "screen_signout_key_backup_offline_title" = "Εξακολουθούν να δημιουργούνται αντίγραφα ασφαλείας των κλειδιών σου"; diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 02dbfdfa63..4200cde50f 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -344,6 +344,7 @@ "rich_text_editor_unindent" = "Unindent"; "rich_text_editor_url_placeholder" = "Link"; "rich_text_editor_a11y_add_attachment" = "Add attachment"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "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."; diff --git a/ElementX/Resources/Localizations/es.lproj/Localizable.strings b/ElementX/Resources/Localizations/es.lproj/Localizable.strings index 1c3482b773..ccbcbd2cdb 100644 --- a/ElementX/Resources/Localizations/es.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/es.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; "banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; -"banner.set_up_recovery.title" = "Set up recovery"; +"banner_set_up_recovery_content" = "Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices."; +"banner_set_up_recovery_title" = "Set up recovery to protect your account"; "common_about" = "Acerca de"; "common_acceptable_use_policy" = "Política de uso aceptable"; "common_advanced_settings" = "Ajustes avanzados"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Verification failed"; "common_verified" = "Verified"; "common_verify_device" = "Verificar dispositivo"; +"common_verify_identity" = "Verify identity"; "common_video" = "Vídeo"; "common_voice_message" = "Mensaje de voz"; "common_waiting" = "Esperando…"; @@ -246,6 +247,8 @@ "common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; "common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "La copia de seguridad del chat no está sincronizada en este momento. Debes confirmar tu clave de recuperación para mantener el acceso a la copia de seguridad del chat."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "Confirma tu clave de recuperación"; "crash_detection_dialog_content" = "%1$@ se cerró inesperadamente la última vez que se lo usaste. ¿Quieres compartir un informe de error con nosotros?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "Quitar sangría"; "rich_text_editor_url_placeholder" = "Enlace"; "rich_text_editor_a11y_add_attachment" = "Adjuntar archivo"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "screen_advanced_settings_element_call_base_url" = "URL base personalizada de Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Define una URL base personalizada para Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL no válida, asegúrate de incluir el protocolo (http/https) y la dirección correcta."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Estás a punto de crear una cuenta en %@"; "screen_advanced_settings_developer_mode" = "Modo desarrollador"; "screen_advanced_settings_developer_mode_description" = "Habilita para tener acceso a características y funcionalidades para desarrolladores."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "Desactiva el editor de texto enriquecido para escribir Markdown manualmente."; "screen_advanced_settings_send_read_receipts" = "Confirmaciones de lectura"; "screen_advanced_settings_send_read_receipts_description" = "Si se desactiva, las confirmaciones de lectura no se enviarán a nadie. Seguirás recibiendo confirmaciones de lectura de otros usuarios."; @@ -458,12 +462,12 @@ "screen_chat_backup_key_backup_action_enable" = "Activar copia de seguridad"; "screen_chat_backup_key_backup_description" = "La copia de seguridad garantiza que no pierdas tu historial de mensajes. %1$@."; "screen_chat_backup_key_backup_title" = "Copia de seguridad"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; "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" = "Cambiar la clave de recuperación"; "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" = "La copia de seguridad de tus chats no está sincronizada ahora mismo."; -"screen_chat_backup_recovery_action_setup" = "Configurar la clave de recuperación"; "screen_chat_backup_recovery_action_setup_description" = "Accede a tus mensajes cifrados si pierdes todos tus dispositivos o cierras sesión de %1$@ en cualquier lugar."; "screen_create_account_title" = "Create account"; "screen_create_new_recovery_key_list_item_1" = "Open %1$@ in a desktop device"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Mostrar menos"; "screen_room_timeline_message_copied" = "Mensaje copiado"; "screen_room_timeline_no_permission_to_post" = "No tienes permiso para publicar en esta sala"; -"screen_room_timeline_reactions_show_less" = "Mostrar menos"; "screen_room_timeline_reactions_show_more" = "Mostrar más"; "screen_room_timeline_read_marker_title" = "Nuevos"; "screen_room_title" = "Chat"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "Compara un conjunto único de emojis."; "screen_session_verification_request_accepted_subtitle" = "Compara los emoji, asegurándote de que aparecen en el mismo orden."; "screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "No push distributors found."; "troubleshoot_notifications_test_unified_push_title" = "Check UnifiedPush"; "a11y_poll" = "Encuesta"; +"banner_set_up_recovery_submit" = "Configurar la recuperación"; "dialog_title_error" = "Error"; "dialog_title_success" = "Terminado"; "notification_fallback_content" = "Notificación"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Desbloquear usuario"; "screen_bug_report_rash_logs_alert_title" = "%1$@ se cerró inesperadamente la última vez que se lo usaste. ¿Quieres compartir un informe de error con nosotros?"; "screen_chat_backup_recovery_action_confirm" = "Introduzca la clave de recuperación"; +"screen_chat_backup_recovery_action_setup" = "Configurar la recuperación"; "screen_create_poll_cancel_confirmation_content_ios" = "Tus cambios no se guardarán"; "screen_create_room_add_people_title" = "Invitar personas"; "screen_create_room_room_name_label" = "Nombre de la sala"; @@ -1029,7 +1033,7 @@ "screen_notification_settings_mentions_section_title" = "Menciones"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Inténtalo de nuevo"; "screen_recovery_key_change_generate_key_description" = "Asegúrate de que puedes guardar tu clave de recuperación en algún lugar seguro"; -"screen_recovery_key_confirm_title" = "Confirma tu clave de recuperación"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Bloquear usuario"; "screen_reset_encryption_password_placeholder" = "Ingresar..."; "screen_room_attachment_source_camera_photo" = "Hacer foto"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Error al procesar el contenido multimedia, por favor inténtalo de nuevo."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Eliminar y prohibir a un miembro"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Únicamente Menciones y Palabras clave"; +"screen_room_timeline_reactions_show_less" = "Mostrar menos"; "screen_roomlist_filter_people" = "Personas"; "screen_server_confirmation_change_server" = "Cambiar el proveedor de la cuenta"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "Cerrar sesión"; "screen_signout_confirmation_dialog_title" = "Cerrar sesión"; "screen_signout_key_backup_offline_title" = "Se sigue guardando una copia de seguridad de tus claves"; diff --git a/ElementX/Resources/Localizations/et.lproj/Localizable.strings b/ElementX/Resources/Localizations/et.lproj/Localizable.strings index 43cb396385..abf2da5bbd 100644 --- a/ElementX/Resources/Localizations/et.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/et.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Sinu koduserver toetab uut ja kiiremat protokolli. Uuendamiseks logi korraks rakendusest välja ja siis tagasi. Mingil hetkel tulevikus vana protokoll eemaldatakse kasutusest ja tehes uuenduse nüüd väldid hilisemat sundkorras uuendust."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Sinu koduserver enam ei toeta vana protokolli. Jätkamaks rakenduse kasutamist palun logi välja ning seejärel tagasi."; "banner_migrate_to_native_sliding_sync_title" = "Saadaval on uuendus"; -"banner.set_up_recovery.content" = "Loo uus taastevõti, mida saad kasutada oma krüptitud sõnumite ajaloo taastamisel olukorras, kus kaotad ligipääsu oma seadmetele."; -"banner.set_up_recovery.title" = "Seadista taastamine"; +"banner_set_up_recovery_content" = "Loo uus taastevõti, mida saad kasutada oma krüptitud sõnumite ajaloo taastamisel olukorras, kus kaotad ligipääsu oma seadmetele."; +"banner_set_up_recovery_title" = "Seadista taastamine"; "common_about" = "Rakenduse teave"; "common_acceptable_use_policy" = "Vastuvõetava kasutamise põhimõtted"; "common_advanced_settings" = "Täiendavad seadistused"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Verifitseerimine ei õnnestunud"; "common_verified" = "Verifitseeritud"; "common_verify_device" = "Verifitseeri seade"; +"common_verify_identity" = "Verifitseeri võrguidentiteet"; "common_video" = "Video"; "common_voice_message" = "Häälsõnum"; "common_waiting" = "Ootame…"; @@ -245,8 +246,10 @@ "common.you" = "Sina"; "common_unable_to_decrypt_insecure_device" = "Saadetud ebaturvalisest seadmest"; "common_unable_to_decrypt_verification_violation" = "Saatja verifitseeritud identiteet on muutunud"; -"confirm_recovery_key_banner_message" = "Sinu vestluste varukoopia pole hetkel sünkroonis. Säilitamaks ligipääsu vestluse varukoopiale palun sisesta oma taastevõti."; -"confirm_recovery_key_banner_title" = "Sisesta oma taastevõti"; +"confirm_recovery_key_banner_message" = "Säilitamaks ligipääsu vestluste ja krüptovõtmete varukoopiale, palun sisesta kinnituseks oma taastevõti."; +"confirm_recovery_key_banner_primary_button_title" = "Sisesta oma taastevõti"; +"confirm_recovery_key_banner_secondary_button_title" = "Kas unustasid oma taastevõtme?"; +"confirm_recovery_key_banner_title" = "Sinu võtmehoidla pole sünkroonis"; "crash_detection_dialog_content" = "%1$@ jooksis kokku viimati, kui seda kasutasid. Kas tahaksid selle kohta meile veateate saata?"; "crypto_identity_change_pin_violation" = "Kasutaja %1$@ võrguidentiteet tundub olema muutunud. %2$@"; "crypto_identity_change_pin_violation_new" = "Kasutaja %1$@ %2$@ võrguidentiteet tundub olema muutunud. %3$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "Eemalda taandrida"; "rich_text_editor_url_placeholder" = "Link"; "rich_text_editor_a11y_add_attachment" = "Lisa manus"; +"rich_text_editor_composer_caption_placeholder" = "Pealkiri, kui soovid lisada…"; "screen_advanced_settings_element_call_base_url" = "Element Calli kohandatud teenuseaadress"; "screen_advanced_settings_element_call_base_url_description" = "Seadista kohandatud teenuseaadress Element Calli jaoks."; "screen_advanced_settings_element_call_base_url_validation_error" = "Vigane url. Palun vaata, et url algaks protokolliga (http/https) ning aadress ise oleks ka õige."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Sa oled loomas kasutajakontot %@ teenuses"; "screen_advanced_settings_developer_mode" = "Arendaja valikud"; "screen_advanced_settings_developer_mode_description" = "Selle eelistuse sisselülitamisel lisanduvad rakendusse arendaja tööks vajalikud valikud."; -"screen_advanced_settings_media_compression_description" = "Optimeeri üleslaadimiseks"; -"screen_advanced_settings_media_compression_title" = "Meedia"; +"screen_advanced_settings_media_compression_description" = "Sellega laadid fotosid ja videoid kiiremini üles ning vähendad andmemahtu"; +"screen_advanced_settings_media_compression_title" = "Optimeeri meedia kvaliteeti"; "screen_advanced_settings_rich_text_editor_description" = "Kui soovid Markdown-vormingut käsitsi lisada, siis lülita vormindatud teksti toimeti välja."; "screen_advanced_settings_send_read_receipts" = "Lugemisteatised"; "screen_advanced_settings_send_read_receipts_description" = "Kui lülitad selle valiku välja, siis mitte keegi enam ei saa sinult lugemisteatisi. Küll aga saad sina teiste kasutajate lugemisteatisi."; @@ -458,12 +462,12 @@ "screen_chat_backup_key_backup_action_enable" = "Lülita võtmete varundamine sisse"; "screen_chat_backup_key_backup_description" = "Salvesta oma krüptoidentiteet ja sõnumite krüptovõtmed turvaliselt serveris. See tagab, et sinu sõnumite ajalugu on alati loetav, ka kõikides uutes seadmetes. %1$@."; "screen_chat_backup_key_backup_title" = "Krüptovõtmete varundus"; +"screen_chat_backup_key_storage_disabled_error" = "Taastamise seadistamiseks peab võtmehoidla olema sisselülitatud."; "screen_chat_backup_key_storage_toggle_description" = "Laadi siin seadmes leiduvad võtmed üles"; "screen_chat_backup_key_storage_toggle_title" = "Luba krüptovõtmete salvestamine"; "screen_chat_backup_recovery_action_change" = "Muuda taastevõtit"; "screen_chat_backup_recovery_action_change_description" = "Kui sa oled kaotanud ligipääsu kõikidele oma olemasolevatele seadmetele, siis sa saad taastevõtme abil taastada ligipääsu oma krüptoidentiteedile ja sõnumite ajaloole."; -"screen_chat_backup_recovery_action_confirm_description" = "Sinu vestluste krüptograafia varukoopia pole hetkel enam sünkroonis."; -"screen_chat_backup_recovery_action_setup" = "Seadista krüptovõtmete varundus"; +"screen_chat_backup_recovery_action_confirm_description" = "Sinu krüptovõtmete varundus pole hetkel enam sünkroonis."; "screen_chat_backup_recovery_action_setup_description" = "Säilita ligipääs oma krüptitud sõnumitele ka siis, kui sa kaotad kõik oma seadmed ja/või logid kõikjal välja rakendusest %1$@."; "screen_create_account_title" = "Loo kasutajakonto"; "screen_create_new_recovery_key_list_item_1" = "Ava %1$@ töölauaga seadmes"; @@ -638,7 +642,7 @@ "screen_recovery_key_change_title" = "Kas muudame taastevõtme?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Loo uus taastevõti"; "screen_recovery_key_confirm_description" = "Palun vaata, et keegi teine ei näeks seda ekraanivaadet!"; -"screen_recovery_key_confirm_error_content" = "Kinnitamaks ligipääsu sinu vestluse varukoopiale, palun proovi uuesti"; +"screen_recovery_key_confirm_error_content" = "Kinnitamaks ligipääsu sinu krüptovõtmete varundusele, palun proovi uuesti"; "screen_recovery_key_confirm_error_title" = "Vigane taastevõti"; "screen_recovery_key_confirm_key_description" = "Kui sul on turvavõti või turvafraas, siis need toimivad ka."; "screen_recovery_key_confirm_key_placeholder" = "Sisesta..."; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Näita vähem"; "screen_room_timeline_message_copied" = "Sõnum on kopeeritud"; "screen_room_timeline_no_permission_to_post" = "Sul pole õigusi siia jututuppa kirjutada"; -"screen_room_timeline_reactions_show_less" = "Näita vähem"; "screen_room_timeline_reactions_show_more" = "Näita rohkem"; "screen_room_timeline_read_marker_title" = "Uus"; "screen_room_title" = "Vestlus"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "Võrdle unikaalset emojide kombinatsiooni"; "screen_session_verification_request_accepted_subtitle" = "Võrdle unikaalset emojide kombinatsiooni ning kontrolli, et nad on täpselt samas järjekorras."; "screen_session_verification_request_details_timestamp" = "Sisselogitud"; -"screen_session_verification_request_failure_subtitle" = "Kas verifitseerimine aegus, teine osapool keeldus vastamast või tekkis vastuste mittevastavus."; "screen_session_verification_request_failure_title" = "Verifitseerimine ei õnnestunud"; "screen_session_verification_request_footer" = "Jätka vaid siis, kui sina algatasid verifitseerimise."; "screen_session_verification_request_subtitle" = "Hoidmaks oma sõnumiajalugu turvatuna verifitseeri teine seade."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "Tõuketeenuse levitajaid ei leidu."; "troubleshoot_notifications_test_unified_push_title" = "Kontrolli UnifiedPushi"; "a11y_poll" = "Küsitlus"; +"banner_set_up_recovery_submit" = "Seadista andmete taastamine"; "dialog_title_error" = "Viga"; "dialog_title_success" = "Õnnestus"; "notification_fallback_content" = "Teavitus"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Eemalda kasutajalt blokeering"; "screen_bug_report_rash_logs_alert_title" = "%1$@ jooksis kokku viimati, kui seda kasutasid. Kas tahaksid selle kohta meile veateate saata?"; "screen_chat_backup_recovery_action_confirm" = "Sisesta taastevõti"; +"screen_chat_backup_recovery_action_setup" = "Seadista andmete taastamine"; "screen_create_poll_cancel_confirmation_content_ios" = "Sinu tehtud muudatused jäävad salvestamata"; "screen_create_room_add_people_title" = "Kutsu osalejaid"; "screen_create_room_room_name_label" = "Jututoa nimi"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Meediafaili töötlemine enne üleslaadimist ei õnnestunud. Palun proovi uuesti."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Eemalda ja sea suhtluskeeld"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Mainimiste ja võtmesõnade alusel"; +"screen_room_timeline_reactions_show_less" = "Näita vähem"; "screen_roomlist_filter_people" = "Inimesed"; "screen_server_confirmation_change_server" = "Muuda teenusepakkujat"; +"screen_session_verification_request_failure_subtitle" = "Kas verifitseerimine aegus, teine osapool keeldus vastamast või tekkis vastuste mittevastavus."; "screen_signout_confirmation_dialog_submit" = "Logi välja"; "screen_signout_confirmation_dialog_title" = "Logi välja"; "screen_signout_key_backup_offline_title" = "Sinu krüptovõtmed on veel varundamisel"; diff --git a/ElementX/Resources/Localizations/fa.lproj/Localizable.strings b/ElementX/Resources/Localizations/fa.lproj/Localizable.strings index fbff44abcb..e8cce2aae0 100644 --- a/ElementX/Resources/Localizations/fa.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/fa.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; "banner_migrate_to_native_sliding_sync_title" = "ارتقا موجود است"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; -"banner.set_up_recovery.title" = "برپایی بازیابی"; +"banner_set_up_recovery_content" = "Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices."; +"banner_set_up_recovery_title" = "برپایی بازیابی"; "common_about" = "درباره"; "common_acceptable_use_policy" = "سیاست استفادهٔ پذیرفتنی"; "common_advanced_settings" = "تنظیمات پیش‌رفته"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Verification failed"; "common_verified" = "Verified"; "common_verify_device" = "تأیید افزاره"; +"common_verify_identity" = "Verify identity"; "common_video" = "ویدیو"; "common_voice_message" = "پیام صوتی"; "common_waiting" = "در انتظار…"; @@ -245,7 +246,9 @@ "common.you" = "شما"; "common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; "common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; -"confirm_recovery_key_banner_message" = "Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup."; +"confirm_recovery_key_banner_message" = "Confirm your recovery key to maintain access to your key storage and message history."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "ورود کلید بازیابیتان"; "crash_detection_dialog_content" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; "crypto_identity_change_pin_violation" = "به نظر می‌رسد هویت %1$@ تغییر کرده. %2$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "تونرفتگی"; "rich_text_editor_url_placeholder" = "پیوند"; "rich_text_editor_a11y_add_attachment" = "افزودن پیوست"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "screen_advanced_settings_element_call_base_url" = "نشانی پایهٔ تماس المنتی سفارشی"; "screen_advanced_settings_element_call_base_url_description" = "تنظمی نشانی پایه‌‌ای سفارشی برای تماس المنتی."; "screen_advanced_settings_element_call_base_url_validation_error" = "Invalid URL, please make sure you include the protocol (http/https) and the correct address."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "دارید حسابی روی %@ می‌سازید"; "screen_advanced_settings_developer_mode" = "حالت توسعه‌دهنده"; "screen_advanced_settings_developer_mode_description" = "Enable to have access to features and functionality for developers."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "از کار انداختن ویرایشگر متن غنی یا نوشتن دستی مارک‌دون."; "screen_advanced_settings_send_read_receipts" = "رسید‌های خواندن"; "screen_advanced_settings_send_read_receipts_description" = "If turned off, your read receipts won't be sent to anyone. You will still receive read receipts from other users."; @@ -458,12 +462,12 @@ "screen_chat_backup_key_backup_action_enable" = "روشن کردن پشتیبان"; "screen_chat_backup_key_backup_description" = "پشتیبان‌ها اطمینان می‌دهند که تاریخچهٔ پیام‌هایتان را از دست نمی‌دهید. %1$@."; "screen_chat_backup_key_backup_title" = "پشتیبان گیری"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; "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" = "تغییر کلید بازیابی"; "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" = "پشتیبان گپتان از هم‌گام بودن در آمده."; -"screen_chat_backup_recovery_action_setup" = "برپایی بازیابی"; "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."; "screen_create_account_title" = "ایجاد حساب"; "screen_create_new_recovery_key_list_item_1" = "گشودن %1$@ در افزارهٔ میزکار"; @@ -538,10 +542,10 @@ "screen_key_backup_disable_confirmation_action_turn_off" = "خاموش کردن"; "screen_key_backup_disable_confirmation_description" = "You will lose your encrypted messages if you are signed out of all devices."; "screen_key_backup_disable_confirmation_title" = "Are you sure you want to turn off backup?"; -"screen_key_backup_disable_description" = "Turning off backup will remove your current encryption key backup and turn off other security features. In this case, you will:"; -"screen_key_backup_disable_description_point_1" = "Not have encrypted message history on new devices"; -"screen_key_backup_disable_description_point_2" = "Lose access to your encrypted messages if you are signed out of %1$@ everywhere"; -"screen_key_backup_disable_title" = "Are you sure you want to turn off backup?"; +"screen_key_backup_disable_description" = "Deleting key storage will remove your cryptographic identity and message keys from the server and turn off the following security features:"; +"screen_key_backup_disable_description_point_1" = "You will not have encrypted message history on new devices"; +"screen_key_backup_disable_description_point_2" = "You will lose access to your encrypted messages if you are signed out of %1$@ everywhere"; +"screen_key_backup_disable_title" = "Are you sure you want to turn off key storage and delete it?"; "screen_login_error_deactivated_account" = "این حساب از کار افتاده است."; "screen_login_error_invalid_credentials" = "نام کاربری یا گذرواژه نامعتبر است"; "screen_login_error_invalid_user_id" = "این یک شناسه کاربری معتبر نیست. قالب صحیح: ‪«@user:homeserver.or"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "نمایش کم‌تر"; "screen_room_timeline_message_copied" = "پیام رونوشت شد"; "screen_room_timeline_no_permission_to_post" = "اجازهٔ فرستادن به این اتاق را ندارید"; -"screen_room_timeline_reactions_show_less" = "نمایش کم‌تر"; "screen_room_timeline_reactions_show_more" = "نمایش بیش‌تر"; "screen_room_timeline_read_marker_title" = "جدید"; "screen_room_title" = "گپ"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "مقایسهٔ مجموعه‌ای یکتا از شکلک‌ها."; "screen_session_verification_request_accepted_subtitle" = "شکلک‌ها را مقایسه کنید، از ترتیب نمایش آنان نیز مطمئن شوید."; "screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "No push distributors found."; "troubleshoot_notifications_test_unified_push_title" = "Check UnifiedPush"; "a11y_poll" = "نظرسنجی"; +"banner_set_up_recovery_submit" = "برپایی بازیابی"; "dialog_title_error" = "خطا"; "dialog_title_success" = "موفّقیت"; "notification_fallback_content" = "آگاهی"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "رفع انسداد کاربر"; "screen_bug_report_rash_logs_alert_title" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; "screen_chat_backup_recovery_action_confirm" = "ورود کلید بازیابی"; +"screen_chat_backup_recovery_action_setup" = "برپایی بازیابی"; "screen_create_poll_cancel_confirmation_content_ios" = "تغییراتتان ذخیره نمی‌شوند"; "screen_create_room_add_people_title" = "دعوت افراد"; "screen_create_room_room_name_label" = "نام اتاق"; @@ -1029,7 +1033,7 @@ "screen_notification_settings_mentions_section_title" = "اشاره‌ها"; "screen_qr_code_login_invalid_scan_state_retry_button" = "تلاش دوباره"; "screen_recovery_key_change_generate_key_description" = "اطمینان از امکان نگه داری کلید بازیابیتان در جایی امن"; -"screen_recovery_key_confirm_title" = "ورود کلید بازیابیتان"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "انسداد کاربر"; "screen_reset_encryption_password_placeholder" = "ورود…"; "screen_room_attachment_source_camera_photo" = "عکس گرفتن"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "پردازش رسانه برای بارگذاری شکست خورد. لطفاً دوباره تلاش کنید."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "برداشت و تحریم عضو"; "screen_room_notification_settings_mode_mentions_and_keywords" = "فقط اشاره‌ها و کلیدواژگان"; +"screen_room_timeline_reactions_show_less" = "نمایش کم‌تر"; "screen_roomlist_filter_people" = "افراد"; "screen_server_confirmation_change_server" = "تغییر فراهم کنندهٔ حساب"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "خروج"; "screen_signout_confirmation_dialog_title" = "خروج"; "screen_signout_key_backup_offline_title" = "کلیدهایتان هنوز در حال پشتیبان گیریند"; diff --git a/ElementX/Resources/Localizations/fr.lproj/Localizable.strings b/ElementX/Resources/Localizations/fr.lproj/Localizable.strings index 58ced059ca..5fe2924ebb 100644 --- a/ElementX/Resources/Localizations/fr.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/fr.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Votre serveur prend désormais en charge un nouveau protocole plus rapide. Déconnectez-vous, puis reconnectez-vous pour effectuer la mise à niveau dès maintenant. En le faisant tout de suite, vous éviterez une déconnexion forcée lorsque l'ancien protocole sera supprimé."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Votre serveur d’accueil ne prend plus en charge l'ancien protocole. Veuillez vous déconnecter puis vous reconnecter pour continuer à utiliser l'application."; "banner_migrate_to_native_sliding_sync_title" = "Mise à niveau disponible"; -"banner.set_up_recovery.content" = "Générez une nouvelle clé de récupération qui peut être utilisée pour restaurer l'historique de vos messages chiffrés au cas où vous perdriez l'accès à vos appareils."; -"banner.set_up_recovery.title" = "Configurer la récupération"; +"banner_set_up_recovery_content" = "Générez une nouvelle clé de récupération qui peut être utilisée pour restaurer l'historique de vos messages chiffrés au cas où vous perdriez l'accès à vos appareils."; +"banner_set_up_recovery_title" = "Configurer la récupération"; "common_about" = "À propos"; "common_acceptable_use_policy" = "Politique d’utilisation acceptable"; "common_advanced_settings" = "Paramètres avancés"; @@ -139,7 +139,7 @@ "common_edited_suffix" = "(modifié)"; "common_editing" = "Édition"; "common_emote" = "* %1$@ %2$@"; -"common_encryption" = "Encryption"; +"common_encryption" = "Chiffrement"; "common_encryption_enabled" = "Chiffrement activé"; "common_enter_your_pin" = "Saisissez votre code PIN"; "common_error" = "Erreur"; @@ -150,7 +150,7 @@ "common_favourited" = "Favorisé"; "common_file" = "Fichier"; "common_forward_message" = "Transférer le message"; -"common_frequently_used" = "Frequently used"; +"common_frequently_used" = "Fréquemment utilisé"; "common_gif" = "GIF"; "common_image" = "Image"; "common_in_reply_to" = "En réponse à %1$@"; @@ -230,9 +230,10 @@ "common_username" = "Nom d’utilisateur"; "common_verification_cancelled" = "Vérification annulée"; "common_verification_complete" = "Vérification terminée"; -"common_verification_failed" = "Verification failed"; +"common_verification_failed" = "Échec de la vérification"; "common_verified" = "Vérifié(e)"; "common_verify_device" = "Vérifier la session"; +"common_verify_identity" = "Verify identity"; "common_video" = "Vidéo"; "common_voice_message" = "Message vocal"; "common_waiting" = "En attente..."; @@ -243,9 +244,11 @@ "common.pinned" = "Épinglé"; "common.send_to" = "Envoyer vers"; "common.you" = "Vous"; -"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; -"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; +"common_unable_to_decrypt_insecure_device" = "Envoyé depuis un appareil non sécurisé"; +"common_unable_to_decrypt_verification_violation" = "L'identité vérifiée de l'expéditeur a changé"; "confirm_recovery_key_banner_message" = "La sauvegarde des conversations est désynchronisée. Vous devez confirmer la clé de récupération pour accéder à votre historique."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "Confirmer votre clé de récupération"; "crash_detection_dialog_content" = "%1$@ s’est arrêté la dernière fois qu’il a été utilisé. Souhaitez-vous partager un rapport d’incident avec nous ?"; "crypto_identity_change_pin_violation" = "L'identité de %1$@ semble avoir changé. %2$@"; @@ -341,21 +344,22 @@ "rich_text_editor_unindent" = "Décaler vers la gauche"; "rich_text_editor_url_placeholder" = "Lien"; "rich_text_editor_a11y_add_attachment" = "Ajouter une pièce jointe"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "screen_advanced_settings_element_call_base_url" = "URL de base pour Element Call personnalisée"; "screen_advanced_settings_element_call_base_url_description" = "Configurer une URL de base pour Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL invalide, assurez-vous d’inclure le protocol (http/https) et l’adresse correcte."; -"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_room_address_section_footer" = "Pour que ce salon soit visible dans le répertoire des salons publics, vous aurez besoin d'une adresse de salon."; +"screen_create_room_room_address_section_title" = "Adresse du salon"; +"screen_create_room_room_visibility_section_title" = "Visibilité du salon"; "screen_create_room_access_section_anyone_option_description" = "Tout le monde peut rejoindre ce salon"; "screen_create_room_access_section_anyone_option_title" = "Tout le monde"; "screen_create_room_access_section_header" = "Accès au salon"; "screen_create_room_access_section_knocking_option_description" = "Tout le monde peut demander à rejoindre le salon, mais un administrateur ou un modérateur devra accepter la demande"; "screen_create_room_access_section_knocking_option_title" = "Demander à rejoindre"; "screen_join_room_cancel_knock_action" = "Annuler la demande"; -"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; -"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; -"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_cancel_knock_alert_confirmation" = "Oui, annuler"; +"screen_join_room_cancel_knock_alert_description" = "Êtes-vous sûr de vouloir annuler votre demande d'accès à ce salon?"; +"screen_join_room_cancel_knock_alert_title" = "Annuler la demande d'adhésion"; "screen_join_room_knock_message_description" = "Message (facultatif)"; "screen_join_room_knock_sent_description" = "Vous recevrez une invitation à rejoindre le salon si votre demande est acceptée."; "screen_join_room_knock_sent_title" = "Demande de rejoindre le salon envoyée"; @@ -376,7 +380,7 @@ "screen_room_pinned_banner_loading_description" = "Chargement du message..."; "screen_room_pinned_banner_view_all_button_title" = "Voir tout"; "screen_room_details_pinned_events_row_title" = "Messages épinglés"; -"screen_roomlist_knock_event_sent_description" = "Request to join sent"; +"screen_roomlist_knock_event_sent_description" = "Demande d'adhésion envoyée"; "screen_timeline_item_menu_send_failure_changed_identity" = "Le message n'a pas été envoyé car l'identité vérifiée de %1$@ a changé."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Le message n'a pas été envoyé car %1$@ n'a pas vérifié tous ses appareils."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message non envoyé car vous n'avez pas vérifié tous vos appareils."; @@ -388,7 +392,7 @@ "screen_account_provider_signup_title" = "Vous êtes sur le point de créer un compte sur %@"; "screen_advanced_settings_developer_mode" = "Mode développeur"; "screen_advanced_settings_developer_mode_description" = "Activer pour pouvoir accéder aux fonctionnalités destinées aux développeurs."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; +"screen_advanced_settings_media_compression_description" = "Optimisé pour le téléchargement"; "screen_advanced_settings_media_compression_title" = "Media"; "screen_advanced_settings_rich_text_editor_description" = "Désactivez l’éditeur de texte enrichi pour saisir manuellement du Markdown."; "screen_advanced_settings_send_read_receipts" = "Accusés de lecture"; @@ -456,14 +460,14 @@ "screen_change_server_title" = "Choisissez votre serveur"; "screen_chat_backup_key_backup_action_disable" = "Désactiver la sauvegarde"; "screen_chat_backup_key_backup_action_enable" = "Activer la sauvegarde"; -"screen_chat_backup_key_backup_description" = "La sauvegarde assure que vous ne perdiez pas l’historique des discussions. %1$@."; -"screen_chat_backup_key_backup_title" = "Sauvegarde"; -"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_key_backup_description" = "Stockez votre identité cryptographique et vos clés de message en toute sécurité sur le serveur. Cela vous permettra de consulter l'historique de vos messages sur tous les nouveaux appareils. %1$@."; +"screen_chat_backup_key_backup_title" = "Stockage des clés"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; +"screen_chat_backup_key_storage_toggle_description" = "Télécharger les clés depuis cet appareil"; +"screen_chat_backup_key_storage_toggle_title" = "Autoriser le stockage des clés"; "screen_chat_backup_recovery_action_change" = "Changer la clé de récupération"; -"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_change_description" = "Récupérez votre identité cryptographique et l'historique de vos messages à l'aide d'une clé de récupération si vous avez perdu tous vos appareils existants."; "screen_chat_backup_recovery_action_confirm_description" = "La sauvegarde des discussions est désynchronisée."; -"screen_chat_backup_recovery_action_setup" = "Configurer la récupération"; "screen_chat_backup_recovery_action_setup_description" = "Accédez à vos messages chiffrés si vous perdez tous vos appareils ou que vous êtes déconnectés de %1$@ partout."; "screen_create_account_title" = "Créer un compte"; "screen_create_new_recovery_key_list_item_1" = "Ouvrez %1$@ sur un ordinateur"; @@ -483,10 +487,10 @@ "screen_create_poll_title" = "Créer un sondage"; "screen_create_room_action_create_room" = "Nouveau salon"; "screen_create_room_error_creating_room" = "Une erreur s’est produite lors de la création du salon"; -"screen_create_room_private_option_description" = "Les messages dans ce salon sont chiffrés. Le chiffrement ne pourra pas être désactivé par la suite."; -"screen_create_room_private_option_title" = "Salon privé (sur invitation seulement)"; -"screen_create_room_public_option_description" = "Les messages ne sont pas chiffrés et n’importe qui peut les lire. Vous pouvez activer le chiffrement ultérieurement."; -"screen_create_room_public_option_title" = "Salon public (tout le monde)"; +"screen_create_room_private_option_description" = "Seules les personnes invitées peuvent accéder à ce salon. Tous les messages sont chiffrés de bout en bout."; +"screen_create_room_private_option_title" = "Salon privé"; +"screen_create_room_public_option_description" = "N'importe qui peut trouver ce salon.\nVous pouvez modifier cela à tout moment dans les paramètres du salon."; +"screen_create_room_public_option_title" = "Salon public"; "screen_create_room_topic_label" = "Sujet (facultatif)"; "screen_deactivate_account_confirmation_dialog_content" = "Veuillez confirmer que vous souhaitez désactiver votre compte. Cette action ne peut pas être annulée."; "screen_deactivate_account_delete_all_messages" = "Supprimer tous mes messages"; @@ -647,14 +651,14 @@ "screen_recovery_key_copied_to_clipboard" = "Clé de récupération copiée"; "screen_recovery_key_generating_key" = "Génération…"; "screen_recovery_key_save_action" = "Enregistrer la clé"; -"screen_recovery_key_save_description" = "Recopier votre clé de récupération dans un endroit sécurisé ou enregistrer la dans un manager de mot de passe."; +"screen_recovery_key_save_description" = "Recopier cette clé de récupération dans un endroit sûr, comme un gestionnaire de mots de passe, une note chiffrée ou un coffre-fort physique."; "screen_recovery_key_save_key_description" = "Taper pour copier la clé"; "screen_recovery_key_save_title" = "Sauvegarder la clé"; "screen_recovery_key_setup_confirmation_description" = "La clé ne pourra plus être affichée après cette étape."; "screen_recovery_key_setup_confirmation_title" = "Avez-vous sauvegardé votre clé de récupération?"; "screen_recovery_key_setup_description" = "Votre sauvegarde est protégée par votre clé de récupération. Si vous avez besoin d’une nouvelle clé après la configuration, vous pourrez en créer une nouvelle en cliquant sur \"Changer la clé de récupération\"."; "screen_recovery_key_setup_generate_key" = "Générer la clé de récupération"; -"screen_recovery_key_setup_generate_key_description" = "Assurez-vous de conserver la clé dans un endroit sûr"; +"screen_recovery_key_setup_generate_key_description" = "Ne partagez cela avec personne !"; "screen_recovery_key_setup_success" = "Sauvegarde mise en place avec succès"; "screen_recovery_key_setup_title" = "Configurer la sauvegarde"; "screen_report_content_block_user_hint" = "Cochez si vous souhaitez masquer tous les messages actuels et futurs de cet utilisateur."; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Afficher moins"; "screen_room_timeline_message_copied" = "Message copié"; "screen_room_timeline_no_permission_to_post" = "Vous n’êtes pas autorisé à publier dans ce salon"; -"screen_room_timeline_reactions_show_less" = "Afficher moins"; "screen_room_timeline_reactions_show_more" = "Afficher plus"; "screen_room_timeline_read_marker_title" = "Nouveau"; "screen_room_title" = "Discussion"; @@ -827,7 +830,7 @@ "screen_session_verification_compare_numbers_title" = "Comparez les nombres"; "screen_session_verification_complete_subtitle" = "Votre nouvelle session est désormais vérifiée. Elle a accès à vos messages chiffrés et les autres utilisateurs la verront identifiée comme fiable."; "screen_session_verification_enter_recovery_key" = "Utiliser la clé de récupération"; -"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_failed_subtitle" = "Soit la demande a expiré, soit elle a été refusée, soit il y a eu une non-concordance de vérification."; "screen_session_verification_open_existing_session_subtitle" = "Prouvez qu’il s’agit bien de vous pour accéder à l’historique de vos messages chiffrés."; "screen_session_verification_open_existing_session_title" = "Ouvrir une session existante"; "screen_session_verification_positive_button_canceled" = "Réessayer la vérification"; @@ -835,13 +838,12 @@ "screen_session_verification_positive_button_verifying_ongoing" = "En attente de correspondance"; "screen_session_verification_ready_subtitle" = "Comparer un groupe unique d’Emojis."; "screen_session_verification_request_accepted_subtitle" = "Comparez les emoji uniques en veillant à ce qu’ils apparaissent dans le même ordre."; -"screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; -"screen_session_verification_request_failure_title" = "Verification failed"; +"screen_session_verification_request_details_timestamp" = "Connecté"; +"screen_session_verification_request_failure_title" = "Échec de la vérification"; "screen_session_verification_request_footer" = "Continuez uniquement si c'est vous qui avez commencé cette vérification."; "screen_session_verification_request_subtitle" = "Vérifiez l'autre appareil pour sécuriser l'historique de vos messages."; -"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; -"screen_session_verification_request_success_title" = "Device verified"; +"screen_session_verification_request_success_subtitle" = "Vous pouvez désormais lire ou envoyer des messages en toute sécurité sur votre autre appareil."; +"screen_session_verification_request_success_title" = "Appareil vérifié"; "screen_session_verification_request_title" = "Vérification demandée"; "screen_session_verification_they_dont_match" = "Ils ne correspondent pas"; "screen_session_verification_they_match" = "Ils correspondent"; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "Aucun distributeur UnifiedPush n'a été trouvé."; "troubleshoot_notifications_test_unified_push_title" = "Vérifier UnifiedPush"; "a11y_poll" = "Sondage"; +"banner_set_up_recovery_submit" = "Configurer la sauvegarde"; "dialog_title_error" = "Erreur"; "dialog_title_success" = "Succès"; "notification_fallback_content" = "Notification"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Débloquer l’utilisateur"; "screen_bug_report_rash_logs_alert_title" = "%1$@ s’est arrêté la dernière fois qu’il a été utilisé. Souhaitez-vous partager un rapport d’incident avec nous ?"; "screen_chat_backup_recovery_action_confirm" = "Utiliser la clé de récupération"; +"screen_chat_backup_recovery_action_setup" = "Configurer la sauvegarde"; "screen_create_poll_cancel_confirmation_content_ios" = "Vos modifications ne seront pas enregistrées"; "screen_create_room_add_people_title" = "Inviter des amis"; "screen_create_room_room_name_label" = "Nom du salon"; @@ -1028,8 +1032,8 @@ "screen_login_subtitle" = "Matrix est un réseau ouvert pour une communication sécurisée et décentralisée."; "screen_notification_settings_mentions_section_title" = "Mentions"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Essayer à nouveau"; -"screen_recovery_key_change_generate_key_description" = "Assurez-vous de conserver la clé dans un endroit sûr"; -"screen_recovery_key_confirm_title" = "Confirmer votre clé de récupération"; +"screen_recovery_key_change_generate_key_description" = "Ne partagez cela avec personne !"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Bloquer l’utilisateur"; "screen_reset_encryption_password_placeholder" = "Saisissez la clé ici…"; "screen_room_attachment_source_camera_photo" = "Prendre une photo"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Échec du traitement des médias à télécharger, veuillez réessayer."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Retirer et bannir ce membre"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Mentions et mots clés uniquement"; +"screen_room_timeline_reactions_show_less" = "Afficher moins"; "screen_roomlist_filter_people" = "Personnes"; "screen_server_confirmation_change_server" = "Changer de fournisseur de compte"; +"screen_session_verification_request_failure_subtitle" = "Soit la demande a expiré, soit elle a été refusée, soit il y a eu une non-concordance de vérification."; "screen_signout_confirmation_dialog_submit" = "Se déconnecter"; "screen_signout_confirmation_dialog_title" = "Se déconnecter"; "screen_signout_key_backup_offline_title" = "Vos clés sont en cours de sauvegarde"; diff --git a/ElementX/Resources/Localizations/hu.lproj/Localizable.strings b/ElementX/Resources/Localizations/hu.lproj/Localizable.strings index a25c0b972f..829d85237a 100644 --- a/ElementX/Resources/Localizations/hu.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/hu.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "A kiszolgálója mostantól egy új, gyorsabb protokollt támogat. A frissítéshez jelentkezzen ki, majd jelentkezzen be újra. Ha ezt most megteszi, elkerülheti a kényszerített kijelentkeztetést a régi protokollt eltávolításakor."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "A Matrix-kiszolgáló már nem támogatja a régi protokollt. Az alkalmazás további használatához jelentkezzen ki és be."; "banner_migrate_to_native_sliding_sync_title" = "Frissítés érhető el"; -"banner.set_up_recovery.content" = "Hozzon létre egy új helyreállítási kulcsot, amellyel visszaállíthatja a titkosított üzenetek előzményeit, ha elveszíti az eszközökhöz való hozzáférést."; -"banner.set_up_recovery.title" = "Helyreállítás beállítása"; +"banner_set_up_recovery_content" = "Hozzon létre egy új helyreállítási kulcsot, amellyel visszaállíthatja a titkosított üzenetek előzményeit, ha elveszíti az eszközökhöz való hozzáférést."; +"banner_set_up_recovery_title" = "Helyreállítás beállítása"; "common_about" = "Névjegy"; "common_acceptable_use_policy" = "Elfogadható használatra vonatkozó szabályzat"; "common_advanced_settings" = "Speciális beállítások"; @@ -139,7 +139,7 @@ "common_edited_suffix" = "(szerkesztve)"; "common_editing" = "Szerkesztés"; "common_emote" = "* %1$@ %2$@"; -"common_encryption" = "Encryption"; +"common_encryption" = "Titkosítás"; "common_encryption_enabled" = "Titkosítás engedélyezve"; "common_enter_your_pin" = "Adja meg a PIN-kódját"; "common_error" = "Hiba"; @@ -150,7 +150,7 @@ "common_favourited" = "Kedvencnek jelölve"; "common_file" = "Fájl"; "common_forward_message" = "Üzenet továbbítása"; -"common_frequently_used" = "Frequently used"; +"common_frequently_used" = "Gyakran használt"; "common_gif" = "GIF"; "common_image" = "Kép"; "common_in_reply_to" = "Válasz erre: %1$@"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Az ellenőrzés sikertelen"; "common_verified" = "Ellenőrizve"; "common_verify_device" = "Eszköz ellenőrzése"; +"common_verify_identity" = "Személyazonosság ellenőrzése"; "common_video" = "Videó"; "common_voice_message" = "Hangüzenet"; "common_waiting" = "Várakozás…"; @@ -243,10 +244,12 @@ "common.pinned" = "Kitűzve"; "common.send_to" = "Címzett"; "common.you" = "Ön"; -"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; -"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; -"confirm_recovery_key_banner_message" = "A csevegés biztonsági mentése nincs szinkronban. Meg kell erősítenie a helyreállítási kulcsát, hogy továbbra is hozzáférjen a csevegés biztonsági mentéséhez."; -"confirm_recovery_key_banner_title" = "Helyreállítási kulcs megerősítése"; +"common_unable_to_decrypt_insecure_device" = "Nem biztonságos eszközről küldve"; +"common_unable_to_decrypt_verification_violation" = "A feladó ellenőrzött személyazonossága megváltozott"; +"confirm_recovery_key_banner_message" = "Erősítse meg a helyreállítási kulcsát, hogy továbbra is hozzáférjen a kulcstárolójához és az üzenetelőzményekhez."; +"confirm_recovery_key_banner_primary_button_title" = "Adja meg a helyreállítási kulcsot"; +"confirm_recovery_key_banner_secondary_button_title" = "Elfelejtette a helyreállítási kulcsot?"; +"confirm_recovery_key_banner_title" = "A kulcstároló nincs szinkronizálva"; "crash_detection_dialog_content" = "Az %1$@ összeomlott a legutóbbi használata óta. Megosztja velünk az összeomlás-jelentést?"; "crypto_identity_change_pin_violation" = "Úgy tűnik, hogy %1$@ személyazonossága megváltozott. %2$@"; "crypto_identity_change_pin_violation_new" = "Úgy tűnik, hogy %1$@ %2$@ személyazonossága megváltozott. %3$@"; @@ -287,8 +290,8 @@ "event_shield_reason_unknown_device" = "Ismeretlen vagy törölt eszköz által titkosítva."; "event_shield_reason_unsigned_device" = "A tulajdonos által nem ellenőrzött eszköz által titkosítva."; "event_shield_reason_unverified_identity" = "Nem ellenőrzött felhasználó által titkosítva."; -"full_screen_intent_banner_message" = "Annak érdekében, hogy soha ne maradjon le egyetlen fontos hívásról sem, módosítsa a beállításokat, hogy engedélyezze a teljes képernyős értesítéseket, amikor a telefon zárolva van."; -"full_screen_intent_banner_title" = "Növelje a hívásélményét"; +"full_screen_intent_banner_message" = "Hogy sose maradjon le egyetlen fontos hívásról sem, a beállításokban engedélyezze a teljes képernyős értesítéseket, amikor a telefon zárolva van."; +"full_screen_intent_banner_title" = "Fokozza a hívásélményét"; "invite_friends_rich_title" = "🔐️ Csatlakozz hozzám itt: %1$@"; "invite_friends_text" = "Beszélgessünk itt: %1$@, %2$@"; "leave_conversation_alert_subtitle" = "Biztos, hogy elhagyja ezt a beszélgetést? Ez a beszélgetés nem nyilvános, és meghívás nélkül nem fog tudni visszacsatlakozni."; @@ -341,12 +344,13 @@ "rich_text_editor_unindent" = "Behúzás nélkül"; "rich_text_editor_url_placeholder" = "Hivatkozás"; "rich_text_editor_a11y_add_attachment" = "Melléklet hozzáadása"; +"rich_text_editor_composer_caption_placeholder" = "Nem kötelező felirat…"; "screen_advanced_settings_element_call_base_url" = "Egyéni Element Call alapwebcím"; "screen_advanced_settings_element_call_base_url_description" = "Egyéni alapwebcím beállítása az Element Callhoz."; "screen_advanced_settings_element_call_base_url_validation_error" = "Érvénytelen webcím, győződjön meg arról, hogy szerepel-e benne a protokoll (http/https), és hogy helyes-e a cím."; -"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_room_address_section_footer" = "Ahhoz, hogy ez a szoba látható legyen a nyilvános szobák címtárában, meg kell adnia a szoba címét."; +"screen_create_room_room_address_section_title" = "Szoba címe"; +"screen_create_room_room_visibility_section_title" = "Szoba láthatósága"; "screen_create_room_access_section_anyone_option_description" = "Bárki csatlakozhat ehhez a szobához"; "screen_create_room_access_section_anyone_option_title" = "Bárki"; "screen_create_room_access_section_header" = "Szobahozzáférés"; @@ -376,7 +380,7 @@ "screen_room_pinned_banner_loading_description" = "Üzenet betöltése…"; "screen_room_pinned_banner_view_all_button_title" = "Összes megtekintése"; "screen_room_details_pinned_events_row_title" = "Kitűzött üzenetek"; -"screen_roomlist_knock_event_sent_description" = "Request to join sent"; +"screen_roomlist_knock_event_sent_description" = "Csatlakozási kérés elküldve"; "screen_timeline_item_menu_send_failure_changed_identity" = "Az üzenet nem lett elküldve, mert %1$@ ellenőrzött személyazonossága megváltozott."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Az üzenet nem lett elküldve, mert %1$@ nem ellenőrizte az összes eszközét."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Az üzenet nem lett elküldve, mert egy vagy több eszközét nem ellenőrizte."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Hamarosan létrehoz egy fiókot itt: %@"; "screen_advanced_settings_developer_mode" = "Fejlesztői mód"; "screen_advanced_settings_developer_mode_description" = "Engedélyezze, hogy elérje a fejlesztőknek szánt funkciókat."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Töltse fel gyorsabban a fényképeket és videókat, valamint csökkentse az adatforgalmat"; +"screen_advanced_settings_media_compression_title" = "Média minőségének optimalizálása"; "screen_advanced_settings_rich_text_editor_description" = "A formázott szöveges szerkesztő letiltása, hogy kézzel írhasson Markdownt."; "screen_advanced_settings_send_read_receipts" = "Olvasási visszaigazolások"; "screen_advanced_settings_send_read_receipts_description" = "Ha ki van kapcsolva, az olvasási visszaigazolások nem lesznek elküldve senkinek. A többi felhasználó olvasási visszaigazolását továbbra is meg fogja kapni."; @@ -456,14 +460,14 @@ "screen_change_server_title" = "Válassza ki a kiszolgálóját"; "screen_chat_backup_key_backup_action_disable" = "Biztonsági mentés kikapcsolása"; "screen_chat_backup_key_backup_action_enable" = "Biztonsági mentés bekapcsolása"; -"screen_chat_backup_key_backup_description" = "A biztonsági mentés biztosítja, hogy ne veszítse el az üzenetelőzményeit. %1$@."; -"screen_chat_backup_key_backup_title" = "Biztonsági mentés"; -"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_key_backup_description" = "Tárolja kriptográfiai személyazonosságát és üzenetkulcsait biztonságosan a kiszolgálón. Ez lehetővé teszi, hogy bármilyen új eszközön megtekinthesse üzenetelőzményeit. %1$@."; +"screen_chat_backup_key_backup_title" = "Kulcstároló"; +"screen_chat_backup_key_storage_disabled_error" = "A helyreállítás beállításához be kell kapcsolni a kulcstárolást."; +"screen_chat_backup_key_storage_toggle_description" = "Kulcsok feltöltése erről az eszközről"; +"screen_chat_backup_key_storage_toggle_title" = "Kulcstárolás engedélyezése"; "screen_chat_backup_recovery_action_change" = "Helyreállítási kulcs módosítása"; -"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" = "A csevegéselőzményei nincsenek szinkronban."; -"screen_chat_backup_recovery_action_setup" = "Helyreállítás beállítása"; +"screen_chat_backup_recovery_action_change_description" = "Ha az összes meglévő eszközét elvesztette, akkor egy helyreállítási kulccsal visszaszerezheti a kriptográfiai személyazonosságát és az üzenetelőzményeit."; +"screen_chat_backup_recovery_action_confirm_description" = "A kulcstároló jelenleg nincs szinkronizálva."; "screen_chat_backup_recovery_action_setup_description" = "Szerezzen hozzáférést a titkosított üzeneteihez, ha elvesztette az összes eszközét, vagy ha mindenütt kijelentkezett az %1$@ből."; "screen_create_account_title" = "Fiók létrehozása"; "screen_create_new_recovery_key_list_item_1" = "Nyissa meg az %1$@et egy asztali eszközön"; @@ -483,10 +487,10 @@ "screen_create_poll_title" = "Szavazás létrehozása"; "screen_create_room_action_create_room" = "Új szoba"; "screen_create_room_error_creating_room" = "Hiba történt a szoba létrehozásakor"; -"screen_create_room_private_option_description" = "A szobában lévő üzenetek titkosítottak. A titkosítást utólag nem lehet kikapcsolni."; -"screen_create_room_private_option_title" = "Privát szoba (csak meghívással)"; -"screen_create_room_public_option_description" = "Az üzenetek nincsenek titkosítva, és bárki elolvashatja őket. A titkosítást később is engedélyezheti."; -"screen_create_room_public_option_title" = "Nyilvános szoba (bárki)"; +"screen_create_room_private_option_description" = "Csak a meghívottak léphetnek be ebbe a szobába. Az összes üzenet végpontok közti titkosítással van védve."; +"screen_create_room_private_option_title" = "Privát szoba"; +"screen_create_room_public_option_description" = "Bárki megtalálhatja ezt a szobát.\nEzt bármikor módosíthatja a szobabeállításokban."; +"screen_create_room_public_option_title" = "Nyilvános szoba"; "screen_create_room_topic_label" = "Téma (nem kötelező)"; "screen_deactivate_account_confirmation_dialog_content" = "Erősítse meg, hogy deaktiválja a fiókját. Ez a művelet nem vonható vissza."; "screen_deactivate_account_delete_all_messages" = "Összes saját üzenet törlése"; @@ -601,15 +605,15 @@ "screen_qr_code_login_connection_note_secure_state_title" = "A kapcsolat nem biztonságos"; "screen_qr_code_login_device_code_subtitle" = "A rendszer kérni fogja, hogy adja meg az alábbi két számjegyet az eszközén."; "screen_qr_code_login_device_code_title" = "Adja meg az alábbi számot a másik eszközén"; -"screen_qr_code_login_device_not_signed_in_scan_state_description" = "Jelentkezzen be a másik eszközére, majd próbálja újra, vagy használjon egy másik eszközt, amelyre már bejelentkezett."; +"screen_qr_code_login_device_not_signed_in_scan_state_description" = "Jelentkezzen be másik eszközére, majd próbálkozzon újra, vagy használjon egy másik, már bejelentkezett eszközt."; "screen_qr_code_login_device_not_signed_in_scan_state_subtitle" = "Más eszköz nincs bejelentkezve"; -"screen_qr_code_login_error_cancelled_subtitle" = "A bejelentkezés megszakadt a másik eszközön."; +"screen_qr_code_login_error_cancelled_subtitle" = "A bejelentkezést megszakították a másik eszközön."; "screen_qr_code_login_error_cancelled_title" = "Bejelentkezési kérés törölve"; -"screen_qr_code_login_error_declined_subtitle" = "A bejelentkezés el lett utasítva a másik eszközön."; +"screen_qr_code_login_error_declined_subtitle" = "A bejelentkezést elutasították a másik eszközön."; "screen_qr_code_login_error_declined_title" = "A bejelentkezés elutasítva"; "screen_qr_code_login_error_expired_subtitle" = "A bejelentkezés lejárt. Próbálja újra."; "screen_qr_code_login_error_expired_title" = "A bejelentkezés nem fejeződött be időben"; -"screen_qr_code_login_error_linking_not_suported_subtitle" = "A másik eszköz nem támogatja a %@ QR-kóddal történő bejelentkezést.\n\nPróbáljon meg kézileg bejelentkezni, vagy olvassa be a QR-kódot egy másik eszközzel."; +"screen_qr_code_login_error_linking_not_suported_subtitle" = "A másik eszköz nem támogatja QR-kóddal történő bejelentkezést az %@be.\n\nPróbáljon meg kézileg bejelentkezni, vagy olvassa be a QR-kódot egy másik eszközzel."; "screen_qr_code_login_error_linking_not_suported_title" = "A QR-kód nem támogatott"; "screen_qr_code_login_error_sliding_sync_not_supported_subtitle" = "A fiókszolgáltatója nem támogatja az %1$@-et."; "screen_qr_code_login_error_sliding_sync_not_supported_title" = "Az %1$@ nem támogatott"; @@ -638,7 +642,7 @@ "screen_recovery_key_change_title" = "Módosítja a helyreállítási kulcsot?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Új helyreállítási kulcs létrehozása"; "screen_recovery_key_confirm_description" = "Győződjön meg arról, hogy senki sem látja ezt a képernyőt!"; -"screen_recovery_key_confirm_error_content" = "Próbálja meg újra megerősíteni a csevegés biztonsági mentéséhez való hozzáférését."; +"screen_recovery_key_confirm_error_content" = "Próbálja újra megerősíteni a kulcstárolóhoz való hozzáférést."; "screen_recovery_key_confirm_error_title" = "Helytelen helyreállítási kulcs"; "screen_recovery_key_confirm_key_description" = "Ha van biztonsági kulcsa vagy biztonsági jelmondata, akkor ez is fog működni."; "screen_recovery_key_confirm_key_placeholder" = "Megadás…"; @@ -647,14 +651,14 @@ "screen_recovery_key_copied_to_clipboard" = "Helyreállítási kulcs másolva"; "screen_recovery_key_generating_key" = "Előállítás…"; "screen_recovery_key_save_action" = "Helyreállítási kulcs mentése"; -"screen_recovery_key_save_description" = "Írja le a helyreállítási kulcsát valami biztonságos helyre, vagy mentse egy jelszókezelőbe."; +"screen_recovery_key_save_description" = "Írja le a helyreállítási kulcsát valami biztonságos helyre, például mentse egy jelszókezelőbe, egy titkosított jegyzetbe vagy egy fizikai széfbe."; "screen_recovery_key_save_key_description" = "Koppintson a helyreállítási kulcs másolásához"; "screen_recovery_key_save_title" = "Mentse el a helyreállítási kulcsát"; "screen_recovery_key_setup_confirmation_description" = "Ezután a lépés után nem fog tudni hozzáférni az új helyreállítási kulcsához."; "screen_recovery_key_setup_confirmation_title" = "Mentette a helyreállítási kulcsát?"; "screen_recovery_key_setup_description" = "A csevegései biztonsági mentését a helyreállítási kulcsa védi. Ha új helyreállítási kulcsra van szüksége a beállítás után, akkor a „Helyreállítási kulcs módosítása” választásával újból létrehozhat egyet."; "screen_recovery_key_setup_generate_key" = "Helyreállítási kulcs előállítása"; -"screen_recovery_key_setup_generate_key_description" = "Gondoskodjon arról, hogy biztonságos helyen tárolja a helyreállítási kulcsát"; +"screen_recovery_key_setup_generate_key_description" = "Ezt ne ossza meg senkivel!"; "screen_recovery_key_setup_success" = "A helyreállítás beállítása sikeres"; "screen_recovery_key_setup_title" = "Helyreállítás beállítása"; "screen_report_content_block_user_hint" = "Jelölje be, ha el akarja rejteni az összes jelenlegi és jövőbeli üzenetet ettől a felhasználótól"; @@ -698,8 +702,8 @@ "screen_room_details_add_topic_title" = "Téma hozzáadása"; "screen_room_details_already_a_member" = "Már tag"; "screen_room_details_already_invited" = "Már meghívták"; -"screen_room_details_badge_encrypted" = "Titkosítva"; -"screen_room_details_badge_not_encrypted" = "Nincs titkosítva"; +"screen_room_details_badge_encrypted" = "Titkosított"; +"screen_room_details_badge_not_encrypted" = "Nem titkosított"; "screen_room_details_badge_public" = "Nyilvános szoba"; "screen_room_details_edit_room_title" = "Szoba szerkesztése"; "screen_room_details_edition_error" = "Ismeretlen hiba történt, és az információkat nem lehetett megváltoztatni."; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Kevesebb megjelenítése"; "screen_room_timeline_message_copied" = "Üzenet másolva"; "screen_room_timeline_no_permission_to_post" = "Nincs jogosultsága arra, hogy bejegyzést tegyen közzé ebben a szobában"; -"screen_room_timeline_reactions_show_less" = "Kevesebb megjelenítése"; "screen_room_timeline_reactions_show_more" = "Több megjelenítése"; "screen_room_timeline_read_marker_title" = "Új"; "screen_room_title" = "Csevegés"; @@ -836,12 +839,11 @@ "screen_session_verification_ready_subtitle" = "Egyedi emodzsik összehasonlítása."; "screen_session_verification_request_accepted_subtitle" = "Hasonlítsa össze az egyedi emodzsikat, meggyőződve arról, hogy azonos a sorrendjük."; "screen_session_verification_request_details_timestamp" = "Bejelentkezve"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; -"screen_session_verification_request_failure_title" = "Verification failed"; +"screen_session_verification_request_failure_title" = "Az ellenőrzés sikertelen"; "screen_session_verification_request_footer" = "Csak akkor folytassa, ha Ön kezdeményezte ezt az ellenőrzést."; "screen_session_verification_request_subtitle" = "Az üzenetelőzmények biztonságának megőrzése érdekében ellenőrizze a másik eszközt."; -"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; -"screen_session_verification_request_success_title" = "Device verified"; +"screen_session_verification_request_success_subtitle" = "Mostantól biztonságosan olvashat vagy küldhet üzeneteket a másik eszközén."; +"screen_session_verification_request_success_title" = "Eszköz ellenőrizve"; "screen_session_verification_request_title" = "Ellenőrzés kérve"; "screen_session_verification_they_dont_match" = "Nem egyeznek"; "screen_session_verification_they_match" = "Megegyeznek"; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "Nem található forgalmazó a leküldéses értesítésekhez."; "troubleshoot_notifications_test_unified_push_title" = "Ellenőrizze a UnifiedPush szolgáltatást"; "a11y_poll" = "Szavazás"; +"banner_set_up_recovery_submit" = "Helyreállítás beállítása"; "dialog_title_error" = "Hiba"; "dialog_title_success" = "Sikeres"; "notification_fallback_content" = "Értesítés"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Felhasználó kitiltásának feloldása"; "screen_bug_report_rash_logs_alert_title" = "Az %1$@ összeomlott a legutóbbi használata óta. Megosztja velünk az összeomlás-jelentést?"; "screen_chat_backup_recovery_action_confirm" = "Adja meg a helyreállítási kulcsot"; +"screen_chat_backup_recovery_action_setup" = "Helyreállítás beállítása"; "screen_create_poll_cancel_confirmation_content_ios" = "A módosításai nem lesznek mentve"; "screen_create_room_add_people_title" = "Ismerősök meghívása"; "screen_create_room_room_name_label" = "Szoba neve"; @@ -1028,8 +1032,8 @@ "screen_login_subtitle" = "A Matrix egy nyitott hálózat a biztonságos, decentralizált kommunikációhoz."; "screen_notification_settings_mentions_section_title" = "Említések"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Próbálja újra"; -"screen_recovery_key_change_generate_key_description" = "Gondoskodjon arról, hogy biztonságos helyen tárolja a helyreállítási kulcsát"; -"screen_recovery_key_confirm_title" = "Helyreállítási kulcs megerősítése"; +"screen_recovery_key_change_generate_key_description" = "Ezt ne ossza meg senkivel!"; +"screen_recovery_key_confirm_title" = "Adja meg a helyreállítási kulcsot"; "screen_report_content_block_user" = "Felhasználó letiltása"; "screen_reset_encryption_password_placeholder" = "Megadás…"; "screen_room_attachment_source_camera_photo" = "Fénykép készítése"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Nem sikerült feldolgozni a feltöltendő médiát, próbálja újra."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Eltávolítás és a tag kitiltása"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Csak említések és kulcsszavak"; +"screen_room_timeline_reactions_show_less" = "Kevesebb megjelenítése"; "screen_roomlist_filter_people" = "Emberek"; "screen_server_confirmation_change_server" = "Fiókszolgáltató módosítása"; +"screen_session_verification_request_failure_subtitle" = "A kérés túllépte az időkorlátot, el lett utasítva, vagy ellenőrzési eltérés történt."; "screen_signout_confirmation_dialog_submit" = "Kijelentkezés"; "screen_signout_confirmation_dialog_title" = "Kijelentkezés"; "screen_signout_key_backup_offline_title" = "A kulcsai mentése még folyamatban van"; diff --git a/ElementX/Resources/Localizations/id.lproj/Localizable.strings b/ElementX/Resources/Localizations/id.lproj/Localizable.strings index b959cf848e..b217ee3984 100644 --- a/ElementX/Resources/Localizations/id.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/id.lproj/Localizable.strings @@ -32,15 +32,15 @@ "action_close" = "Tutup"; "action_complete_verification" = "Selesaikan verifikasi"; "action_confirm" = "Konfirmasi"; -"action_confirm_password" = "Confirm password"; +"action_confirm_password" = "Konfirmasi kata sandi"; "action_continue" = "Lanjutkan"; "action_copy" = "Salin"; "action_copy_link" = "Salin tautan"; "action_copy_link_to_message" = "Salin tautan ke pesan"; "action_create" = "Buat"; "action_create_a_room" = "Buat ruangan"; -"action_deactivate" = "Deactivate"; -"action_deactivate_account" = "Deactivate account"; +"action_deactivate" = "Nonaktifkan"; +"action_deactivate_account" = "Nonaktifkan akun"; "action_decline" = "Tolak"; "action_delete_poll" = "Hapus pemungutan suara"; "action_disable" = "Nonaktifkan"; @@ -54,7 +54,7 @@ "action_forgot_password" = "Lupa kata sandi?"; "action_forward" = "Teruskan"; "action_go_back" = "Kembali"; -"action_ignore" = "Ignore"; +"action_ignore" = "Abaikan"; "action_invite" = "Undang"; "action_invite_friends" = "Undang orang-orang"; "action_invite_friends_to_app" = "Undang orang-orang ke %1$@"; @@ -95,7 +95,7 @@ "action_send_message" = "Kirim pesan"; "action_share" = "Bagikan"; "action_share_link" = "Bagikan tautan"; -"action_show" = "Show"; +"action_show" = "Tampilkan"; "action_sign_in_again" = "Masuk lagi"; "action_signout" = "Keluar dari akun"; "action_signout_anyway" = "Keluar saja"; @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Server Anda kini mendukung protokol baru yang lebih cepat. Keluar dan masuk lagi untuk memperbarui sekarang. Melakukan hal ini sekarang akan membantu Anda menghindari keluar paksa saat protokol lama dihapus nantinya."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Homeserver Anda tidak lagi mendukung protokol lama. Silakan keluar dan masuk kembali untuk terus menggunakan aplikasi."; "banner_migrate_to_native_sliding_sync_title" = "Peningkatan tersedia"; -"banner.set_up_recovery.content" = "Buat kunci pemulihan baru yang dapat digunakan untuk memulihkan riwayat pesan terenkripsi Anda jika Anda kehilangan akses ke perangkat Anda."; -"banner.set_up_recovery.title" = "Siapkan pemulihan"; +"banner_set_up_recovery_content" = "Buat kunci pemulihan baru yang dapat digunakan untuk memulihkan riwayat pesan terenkripsi Anda jika Anda kehilangan akses ke perangkat Anda."; +"banner_set_up_recovery_title" = "Siapkan pemulihan"; "common_about" = "Tentang"; "common_acceptable_use_policy" = "Kebijakan penggunaan wajar"; "common_advanced_settings" = "Pengaturan tingkat lanjut"; @@ -134,12 +134,12 @@ "common_dark" = "Gelap"; "common_decryption_error" = "Kesalahan dekripsi"; "common_developer_options" = "Opsi pengembang"; -"common_device_id" = "Device ID"; +"common_device_id" = "ID Perangkat"; "common_direct_chat" = "Obrolan langsung"; "common_edited_suffix" = "(disunting)"; "common_editing" = "Penyuntingan"; "common_emote" = "* %1$@ %2$@"; -"common_encryption" = "Encryption"; +"common_encryption" = "Enkripsi"; "common_encryption_enabled" = "Enkripsi diaktifkan"; "common_enter_your_pin" = "Masukkan PIN Anda"; "common_error" = "Eror"; @@ -150,7 +150,7 @@ "common_favourited" = "Difavoritkan"; "common_file" = "Berkas"; "common_forward_message" = "Teruskan pesan"; -"common_frequently_used" = "Frequently used"; +"common_frequently_used" = "Sering digunakan"; "common_gif" = "GIF"; "common_image" = "Gambar"; "common_in_reply_to" = "Membalas kepada %1$@"; @@ -230,26 +230,29 @@ "common_username" = "Nama pengguna"; "common_verification_cancelled" = "Verifikasi dibatalkan"; "common_verification_complete" = "Verifikasi selesai"; -"common_verification_failed" = "Verification failed"; -"common_verified" = "Verified"; +"common_verification_failed" = "Verifikasi gagal"; +"common_verified" = "Terverifikasi"; "common_verify_device" = "Verifikasi perangkat"; +"common_verify_identity" = "Verifikasi identitas"; "common_video" = "Video"; "common_voice_message" = "Pesan suara"; "common_waiting" = "Menunggu…"; "common_waiting_for_decryption_key" = "Menunggu pesan ini"; -"common.copied_to_clipboard" = "Copied to clipboard"; +"common.copied_to_clipboard" = "Disalin ke papan klip"; "common.do_not_show_this_again" = "Jangan tampilkan ini lagi"; "common.open_source_licenses" = "Lisensi sumber terbuka"; "common.pinned" = "Disematkan"; "common.send_to" = "Kirim ke"; -"common.you" = "You"; -"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; -"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; -"confirm_recovery_key_banner_message" = "Cadangan percakapan Anda saat ini tidak tersinkron. Anda perlu mengonfirmasi kunci pemulihan Anda untuk tetap memiliki akses ke cadangan percakapan Anda."; -"confirm_recovery_key_banner_title" = "Konfirmasi kunci pemulihan Anda"; +"common.you" = "Anda"; +"common_unable_to_decrypt_insecure_device" = "Dikirim dari perangkat yang tidak aman"; +"common_unable_to_decrypt_verification_violation" = "Identitas terverifikasi pengirim telah berubah"; +"confirm_recovery_key_banner_message" = "Konfirmasikan kunci pemulihan Anda untuk mempertahankan akses ke penyimpanan kunci dan riwayat pesan Anda."; +"confirm_recovery_key_banner_primary_button_title" = "Masukkan kunci pemulihan Anda"; +"confirm_recovery_key_banner_secondary_button_title" = "Lupa kunci pemulihan Anda?"; +"confirm_recovery_key_banner_title" = "Penyimpanan kunci Anda tidak sinkron"; "crash_detection_dialog_content" = "%1$@ mengalami kemogokan saat terakhir kali digunakan. Apakah Anda ingin berbagi laporan kerusakan dengan kami?"; -"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; -"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation" = "Identitas %1$@ tampaknya telah berubah. %2$@"; +"crypto_identity_change_pin_violation_new" = "Identitas %1$@ yang %2$@ tampaknya telah berubah. %3$@"; "crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Supaya aplikasinya dapat menggunakan kamera, berikan izin dalam pengaturan sistem."; "dialog_permission_generic" = "Silakan memberikan izin dalam pengaturan sistem."; @@ -271,7 +274,7 @@ "emoji_picker_category_people" = "Senyuman & Orang"; "emoji_picker_category_places" = "Wisata & Tempat"; "emoji_picker_category_symbols" = "Simbol"; -"error_account_creation_not_possible" = "Your homeserver needs to be upgraded to support Matrix Authentication Service and account creation."; +"error_account_creation_not_possible" = "Homeserver Anda perlu ditingkatkan untuk mendukung Matrix Authentication Service dan pembuatan akun."; "error_failed_creating_the_permalink" = "Gagal membuat tautan permanen"; "error_failed_loading_map" = "%1$@ tidak dapat memuat peta. Silakan coba lagi nanti."; "error_failed_loading_messages" = "Gagal memuat pesan"; @@ -304,12 +307,12 @@ "notification_incoming_call" = "Panggilan masuk"; "notification_inline_reply_failed" = "** Gagal mengirim — silakan buka ruangan"; "notification_invite_body" = "Mengundang Anda untuk mengobrol"; -"notification_invite_body_with_sender" = "%1$@ invited you to chat"; +"notification_invite_body_with_sender" = "%1$@ mengundang Anda untuk mengobrol"; "notification_mentioned_you_body" = "Menyebutkan Anda: %1$@"; "notification_new_messages" = "Pesan Baru"; "notification_reaction_body" = "Menghapus dengan %1$@"; "notification_room_invite_body" = "Mengundang Anda untuk bergabung ke ruangan"; -"notification_room_invite_body_with_sender" = "%1$@ invited you to join the room"; +"notification_room_invite_body_with_sender" = "%1$@ mengundang Anda untuk bergabung dengan ruangan"; "notification_sender_me" = "Saya"; "notification_sender_mention_reply" = "%1$@ disebut atau dibalas"; "notification_test_push_notification_content" = "Anda sedang melihat pemberitahuan ini! Klik saya!"; @@ -341,45 +344,46 @@ "rich_text_editor_unindent" = "Hapus indentasi"; "rich_text_editor_url_placeholder" = "Tautan"; "rich_text_editor_a11y_add_attachment" = "Tambahkan lampiran"; +"rich_text_editor_composer_caption_placeholder" = "Keterangan opsional..."; "screen_advanced_settings_element_call_base_url" = "URL dasar Element Call khusus"; "screen_advanced_settings_element_call_base_url_description" = "Tetapkan URL dasar khusus untuk Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL tidak valid, pastikan Anda menyertakan protokol (http/https) dan alamat yang benar."; -"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"; -"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; -"screen_create_room_access_section_knocking_option_title" = "Ask to join"; -"screen_join_room_cancel_knock_action" = "Cancel request"; -"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; -"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; -"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; -"screen_join_room_knock_message_description" = "Message (optional)"; -"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; -"screen_join_room_knock_sent_title" = "Request to join sent"; +"screen_create_room_room_address_section_footer" = "Supaya ruangan ini terlihat di direktori ruangan publik, Anda memerlukan alamat ruangan."; +"screen_create_room_room_address_section_title" = "Alamat ruangan"; +"screen_create_room_room_visibility_section_title" = "Keterlihatan ruangan"; +"screen_create_room_access_section_anyone_option_description" = "Siapa pun dapat bergabung dengan ruangan ini"; +"screen_create_room_access_section_anyone_option_title" = "Siapa pun"; +"screen_create_room_access_section_header" = "Akses Ruangan"; +"screen_create_room_access_section_knocking_option_description" = "Siapa pun dapat meminta untuk bergabung dengan ruangan tetapi administrator atau moderator harus menerima permintaan tersebut"; +"screen_create_room_access_section_knocking_option_title" = "Minta untuk bergabung"; +"screen_join_room_cancel_knock_action" = "Batalkan permintaan"; +"screen_join_room_cancel_knock_alert_confirmation" = "Ya, batalkan"; +"screen_join_room_cancel_knock_alert_description" = "Apakah Anda yakin ingin membatalkan permintaan Anda untuk bergabung dengan ruangan ini?"; +"screen_join_room_cancel_knock_alert_title" = "Batalkan permintaan untuk bergabung"; +"screen_join_room_knock_message_description" = "Pesan (opsional)"; +"screen_join_room_knock_sent_description" = "Anda akan menerima undangan untuk bergabung dengan ruangan jika permintaan Anda diterima."; +"screen_join_room_knock_sent_title" = "Permintaan untuk bergabung dikirim"; "screen_pinned_timeline_empty_state_description" = "Tekan pesan dan pilih “%1$@” untuk disertakan di sini."; "screen_pinned_timeline_empty_state_headline" = "Sematkan pesan penting agar mudah ditemukan"; "screen_reset_encryption_password_error" = "Terjadi kesalahan yang tidak diketahui. Harap periksa apakah kata sandi akun Anda sudah benar dan coba lagi."; -"screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; -"screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; -"screen_resolve_send_failure_changed_identity_title" = "Your message was not sent because %1$@’s verified identity has changed"; +"screen_resolve_send_failure_changed_identity_primary_button_title" = "Tarik verifikasi dan kirim"; +"screen_resolve_send_failure_changed_identity_subtitle" = "Anda dapat menarik verifikasi dan tetap mengirim pesan ini, atau Anda dapat membatalkan untuk saat ini dan mencoba lagi nanti setelah memverifikasi ulang %1$@."; +"screen_resolve_send_failure_changed_identity_title" = "Pesan Anda tidak terkirim karena identitas terverifikasi %1$@ telah berubah"; "screen_resolve_send_failure_unsigned_device_primary_button_title" = "Kirim pesan saja"; "screen_resolve_send_failure_unsigned_device_subtitle" = "%1$@ menggunakan satu atau beberapa perangkat yang belum diverifikasi. Anda tetap dapat mengirim pesan, atau Anda dapat membatalkan untuk saat ini dan mencoba lagi nanti setelah %2$@ telah memverifikasi semua perangkat mereka."; "screen_resolve_send_failure_unsigned_device_title" = "Pesan Anda tidak terkirim karena %1$@ belum memverifikasi semua perangkat"; -"screen_resolve_send_failure_you_unsigned_device_subtitle" = "One or more of your devices are unverified. You can send the message anyway, or you can cancel for now and try again later after you have verified all of your devices."; -"screen_resolve_send_failure_you_unsigned_device_title" = "Your message was not sent because you have not verified one or more of your devices"; +"screen_resolve_send_failure_you_unsigned_device_subtitle" = "Satu atau beberapa perangkat Anda tidak terverifikasi. Anda tetap dapat mengirim pesan, atau Anda dapat membatalkannya dan mencoba lagi nanti setelah Anda memverifikasi semua perangkat."; +"screen_resolve_send_failure_you_unsigned_device_title" = "Pesan Anda tidak terkirim karena Anda belum memverifikasi satu atau beberapa perangkat Anda"; "screen_room_mentions_at_room_subtitle" = "Beri tahu seluruh ruangan"; "screen_room_pinned_banner_indicator" = "%1$@ dari %2$@"; "screen_room_pinned_banner_indicator_description" = "%1$@ Pesan yang disematkan"; "screen_room_pinned_banner_loading_description" = "Memuat pesan…"; "screen_room_pinned_banner_view_all_button_title" = "Lihat Semua"; "screen_room_details_pinned_events_row_title" = "Pesan yang disematkan"; -"screen_roomlist_knock_event_sent_description" = "Request to join sent"; +"screen_roomlist_knock_event_sent_description" = "Permintaan untuk bergabung dikirim"; "screen_timeline_item_menu_send_failure_changed_identity" = "Pesan tidak terkirim karena identitas terverifikasi %1$@ telah berubah."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Pesan tidak terkirim karena %1$@ belum memverifikasi semua perangkat."; -"screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; +"screen_timeline_item_menu_send_failure_you_unsigned_device" = "Pesan tidak terkirim karena Anda belum memverifikasi satu atau beberapa perangkat Anda."; "screen_account_provider_form_hint" = "Alamat homeserver"; "screen_account_provider_form_notice" = "Masukkan istilah pencarian atau alamat domain."; "screen_account_provider_form_subtitle" = "Cari perusahaan, komunitas, atau server pribadi."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Anda akan membuat akun di %@"; "screen_advanced_settings_developer_mode" = "Mode pengembang"; "screen_advanced_settings_developer_mode_description" = "Aktifkan untuk mengakses fitur dan fungsi untuk para pengembang."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Unggah foto dan video lebih cepat dan kurangi penggunaan data"; +"screen_advanced_settings_media_compression_title" = "Optimalkan kualitas media"; "screen_advanced_settings_rich_text_editor_description" = "Nonaktifkan penyunting teks kaya untuk mengetik Markdown secara manual."; "screen_advanced_settings_send_read_receipts" = "Laporan dibaca"; "screen_advanced_settings_send_read_receipts_description" = "Jika dimatikan, laporan dibaca Anda tidak akan dikirim kepada siapa pun. Anda masih akan menerima laporan dibaca dari pengguna lain."; @@ -458,14 +462,14 @@ "screen_chat_backup_key_backup_action_enable" = "Nyalakan pencadangan"; "screen_chat_backup_key_backup_description" = "Simpan identitas kriptografi Anda dan kunci-kunci pesan secara aman di server. Ini akan memungkinkan Anda untuk melihat riwayat pesan Anda di perangkat yang baru. %1$@."; "screen_chat_backup_key_backup_title" = "Penyimpanan kunci"; -"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_key_storage_disabled_error" = "Penyimpanan kunci harus diaktifkan untuk menyiapkan pemulihan."; +"screen_chat_backup_key_storage_toggle_description" = "Unggah kunci dari perangkat ini"; +"screen_chat_backup_key_storage_toggle_title" = "Izinkan penyimpanan kunci"; "screen_chat_backup_recovery_action_change" = "Ubah kunci pemulihan"; -"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" = "Pencadangan percakapan Anda saat ini tidak tersinkron."; -"screen_chat_backup_recovery_action_setup" = "Siapkan pemulihan"; +"screen_chat_backup_recovery_action_change_description" = "Pulihkan identitas kriptografi dan riwayat pesan Anda dengan kunci pemulihan jika Anda kehilangan semua perangkat yang ada."; +"screen_chat_backup_recovery_action_confirm_description" = "Penyimpanan kunci Anda saat ini tidak sinkron."; "screen_chat_backup_recovery_action_setup_description" = "Dapatkan akses ke pesan terenkripsi Anda jika Anda kehilangan semua perangkat Anda atau keluar dari %1$@ di mana pun."; -"screen_create_account_title" = "Create account"; +"screen_create_account_title" = "Buat akun"; "screen_create_new_recovery_key_list_item_1" = "Buka %1$@ di perangkat desktop"; "screen_create_new_recovery_key_list_item_2" = "Masuk ke akun Anda lagi"; "screen_create_new_recovery_key_list_item_3" = "Saat diminta untuk memverifikasi perangkat Anda, pilih %1$@"; @@ -488,17 +492,17 @@ "screen_create_room_public_option_description" = "Siapa pun dapat mencari ruangan ini.\nAnda dapat mengubah ini kapan pun dalam pengaturan ruangan."; "screen_create_room_public_option_title" = "Ruangan publik"; "screen_create_room_topic_label" = "Topik (opsional)"; -"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"; -"screen_deactivate_account_delete_all_messages_notice" = "Warning: Future users may see incomplete conversations."; -"screen_deactivate_account_description" = "Deactivating your account is %1$@, it will:"; -"screen_deactivate_account_description_bold_part" = "irreversible"; -"screen_deactivate_account_list_item_1" = "%1$@ your account (you can't log back in, and your ID can't be reused)."; -"screen_deactivate_account_list_item_1_bold_part" = "Permanently disable"; -"screen_deactivate_account_list_item_2" = "Remove you from all chat rooms."; -"screen_deactivate_account_list_item_3" = "Delete your account information from our identity server."; -"screen_deactivate_account_list_item_4" = "Your messages will still be visible to registered users but won’t be available to new or unregistered users if you choose to delete them."; -"screen_deactivate_account_title" = "Deactivate account"; +"screen_deactivate_account_confirmation_dialog_content" = "Harap konfirmasi bahwa Anda ingin menonaktifkan akun Anda. Tindakan ini tidak dapat diurungkan."; +"screen_deactivate_account_delete_all_messages" = "Hapus semua pesan saya"; +"screen_deactivate_account_delete_all_messages_notice" = "Peringatan: Pengguna masa depan mungkin melihat percakapan yang tidak lengkap."; +"screen_deactivate_account_description" = "Penonaktifan akun Anda %1$@, ini akan:"; +"screen_deactivate_account_description_bold_part" = "tidak dapat diurungkan"; +"screen_deactivate_account_list_item_1" = "%1$@ akun Anda (Anda tidak dapat masuk kembali, dan ID Anda tidak dapat digunakan kembali)."; +"screen_deactivate_account_list_item_1_bold_part" = "Nonaktifkan secara permanen"; +"screen_deactivate_account_list_item_2" = "Mengeluarkan Anda dari semua ruangan obrolan."; +"screen_deactivate_account_list_item_3" = "Hapus informasi akun Anda dari server identitas kami."; +"screen_deactivate_account_list_item_4" = "Pesan Anda akan tetap terlihat oleh pengguna terdaftar tetapi tidak akan tersedia bagi pengguna baru atau tidak terdaftar jika Anda memilih untuk menghapusnya."; +"screen_deactivate_account_title" = "Nonaktifkan akun"; "screen_edit_poll_delete_confirmation" = "Apakah Anda yakin ingin menghapus pemungutan suara ini?"; "screen_edit_profile_display_name" = "Nama tampilan"; "screen_edit_profile_display_name_placeholder" = "Nama tampilan Anda"; @@ -619,7 +623,7 @@ "screen_qr_code_login_initial_state_item_3" = "Pilih %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Tautkan perangkat baru”"; "screen_qr_code_login_initial_state_item_4" = "Pindai kode QR dengan perangkat ini"; -"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; +"screen_qr_code_login_initial_state_subtitle" = "Hanya tersedia jika penyedia akun Anda mendukungnya."; "screen_qr_code_login_initial_state_title" = "Buka %1$@ di perangkat lain untuk mendapatkan kode QR"; "screen_qr_code_login_invalid_scan_state_description" = "Gunakan kode QR yang ditampilkan di perangkat lain."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Kode QR salah"; @@ -638,7 +642,7 @@ "screen_recovery_key_change_title" = "Ubah kunci pemulihan?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Buat kunci pemulihan baru"; "screen_recovery_key_confirm_description" = "Pastikan tidak ada yang bisa melihat layar ini!"; -"screen_recovery_key_confirm_error_content" = "Silakan coba lagi untuk mengonfirmasi akses ke cadangan percakapan Anda."; +"screen_recovery_key_confirm_error_content" = "Silakan coba lagi untuk mengonfirmasi akses ke penyimpanan kunci Anda."; "screen_recovery_key_confirm_error_title" = "Kunci pemulihan salah"; "screen_recovery_key_confirm_key_description" = "Jika Anda memiliki kunci keamanan atau frasa keamanan, ini juga bisa digunakan."; "screen_recovery_key_confirm_key_placeholder" = "Masukkan..."; @@ -728,8 +732,8 @@ "screen_room_member_details_unblock_alert_action" = "Buka blokir"; "screen_room_member_details_unblock_alert_description" = "Anda akan dapat melihat semua pesan dari mereka lagi."; "screen_room_member_details_unblock_user" = "Buka blokir pengguna"; -"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; -"screen_room_member_details_verify_button_title" = "Verify %1$@"; +"screen_room_member_details_verify_button_subtitle" = "Gunakan aplikasi web untuk memverifikasi pengguna ini."; +"screen_room_member_details_verify_button_title" = "Verifikasi %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Cekal"; "screen_room_member_list_ban_member_confirmation_description" = "Mereka tidak akan dapat bergabung ke ruangan ini lagi jika diundang."; "screen_room_member_list_ban_member_confirmation_title" = "Apakah Anda yakin ingin mencekal anggota ini?"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Tampilkan lebih sedikit"; "screen_room_timeline_message_copied" = "Pesan disalin"; "screen_room_timeline_no_permission_to_post" = "Anda tidak memiliki izin untuk mengirim di ruangan ini"; -"screen_room_timeline_reactions_show_less" = "Tampilkan lebih sedikit"; "screen_room_timeline_reactions_show_more" = "Tampilkan lebih banyak"; "screen_room_timeline_read_marker_title" = "Baru"; "screen_room_title" = "Obrolan"; @@ -827,7 +830,7 @@ "screen_session_verification_compare_numbers_title" = "Bandingkan angka"; "screen_session_verification_complete_subtitle" = "Sesi baru Anda sekarang diverifikasi. Ini memiliki akses ke pesan terenkripsi Anda, dan pengguna lain akan melihatnya sebagai tepercaya."; "screen_session_verification_enter_recovery_key" = "Masukkan kunci pemulihan"; -"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_failed_subtitle" = "Entah permintaan habis waktu, permintaan ditolak, atau ada ketidakcocokan verifikasi."; "screen_session_verification_open_existing_session_subtitle" = "Buktikan bahwa ini memang Anda untuk mengakses riwayat pesan terenkripsi Anda."; "screen_session_verification_open_existing_session_title" = "Buka sesi yang sudah ada"; "screen_session_verification_positive_button_canceled" = "Verifikasi ulang"; @@ -835,14 +838,13 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Menunggu untuk mencocokkan"; "screen_session_verification_ready_subtitle" = "Bandingkan satu set emoji yang unik."; "screen_session_verification_request_accepted_subtitle" = "Bandingkan emoji unik, dan pastikan emoji tersebut muncul dalam urutan yang sama."; -"screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; -"screen_session_verification_request_failure_title" = "Verification failed"; -"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; -"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; -"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; -"screen_session_verification_request_success_title" = "Device verified"; -"screen_session_verification_request_title" = "Verification requested"; +"screen_session_verification_request_details_timestamp" = "Sudah masuk"; +"screen_session_verification_request_failure_title" = "Verifikasi gagal"; +"screen_session_verification_request_footer" = "Lanjutkan hanya jika Anda memulai verifikasi ini."; +"screen_session_verification_request_subtitle" = "Verifikasi perangkat lain untuk menjaga riwayat pesan Anda tetap aman."; +"screen_session_verification_request_success_subtitle" = "Sekarang Anda dapat membaca atau mengirim pesan dengan aman di perangkat Anda yang lain."; +"screen_session_verification_request_success_title" = "Perangkat diverifikasi"; +"screen_session_verification_request_title" = "Verifikasi diminta"; "screen_session_verification_they_dont_match" = "Mereka tidak cocok"; "screen_session_verification_they_match" = "Mereka cocok"; "screen_session_verification_waiting_to_accept_subtitle" = "Terima permintaan untuk memulai proses verifikasi di sesi Anda yang lain untuk melanjutkan."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "Tidak ada distributor notifikasi dorongan yang ditemukan."; "troubleshoot_notifications_test_unified_push_title" = "Periksa UnifiedPush"; "a11y_poll" = "Pemungutan suara"; +"banner_set_up_recovery_submit" = "Siapkan pemulihan"; "dialog_title_error" = "Eror"; "dialog_title_success" = "Berhasil"; "notification_fallback_content" = "Notifikasi"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Buka blokir pengguna"; "screen_bug_report_rash_logs_alert_title" = "%1$@ mengalami kemogokan saat terakhir kali digunakan. Apakah Anda ingin berbagi laporan kerusakan dengan kami?"; "screen_chat_backup_recovery_action_confirm" = "Masukkan kunci pemulihan"; +"screen_chat_backup_recovery_action_setup" = "Siapkan pemulihan"; "screen_create_poll_cancel_confirmation_content_ios" = "Perubahan Anda tidak akan disimpan"; "screen_create_room_add_people_title" = "Undang orang-orang"; "screen_create_room_room_name_label" = "Nama ruangan"; @@ -1029,7 +1033,7 @@ "screen_notification_settings_mentions_section_title" = "Sebutan"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Coba lagi"; "screen_recovery_key_change_generate_key_description" = "Jangan bagikan ini kepada siapa pun!"; -"screen_recovery_key_confirm_title" = "Konfirmasi kunci pemulihan Anda"; +"screen_recovery_key_confirm_title" = "Masukkan kunci pemulihan Anda"; "screen_report_content_block_user" = "Blokir pengguna"; "screen_reset_encryption_password_placeholder" = "Masukkan..."; "screen_room_attachment_source_camera_photo" = "Ambil foto"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Gagal memproses media untuk diunggah, silakan coba lagi."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Keluarkan dan cekal anggota"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Sebutan dan Kata Kunci saja"; +"screen_room_timeline_reactions_show_less" = "Tampilkan lebih sedikit"; "screen_roomlist_filter_people" = "Orang"; "screen_server_confirmation_change_server" = "Ubah penyedia akun"; +"screen_session_verification_request_failure_subtitle" = "Entah permintaan habis waktu, permintaan ditolak, atau ada ketidakcocokan verifikasi."; "screen_signout_confirmation_dialog_submit" = "Keluar dari akun"; "screen_signout_confirmation_dialog_title" = "Keluar dari akun"; "screen_signout_key_backup_offline_title" = "Kunci Anda masih dicadangkan"; diff --git a/ElementX/Resources/Localizations/it.lproj/Localizable.strings b/ElementX/Resources/Localizations/it.lproj/Localizable.strings index 69b8ea3d0f..ef7bddaf9f 100644 --- a/ElementX/Resources/Localizations/it.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/it.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Il tuo server ora supporta un nuovo protocollo più veloce. Esci e rientra per effettuare l'aggiornamento. Se lo fai ora, eviterai una disconnessione forzata quando il vecchio protocollo verrà rimosso in seguito."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Il tuo homeserver non supporta più il vecchio protocollo. Esci e rientra per continuare a usare l'app."; "banner_migrate_to_native_sliding_sync_title" = "Aggiornamento disponibile"; -"banner.set_up_recovery.content" = "Genera una nuova chiave di recupero che può essere usata per ripristinare la cronologia dei messaggi crittografati nel caso in cui tu perda l'accesso ai tuoi dispositivi."; -"banner.set_up_recovery.title" = "Configura il ripristino"; +"banner_set_up_recovery_content" = "Genera una nuova chiave di recupero che può essere usata per ripristinare la cronologia dei messaggi crittografati nel caso in cui tu perda l'accesso ai tuoi dispositivi."; +"banner_set_up_recovery_title" = "Configura il ripristino"; "common_about" = "Informazioni"; "common_acceptable_use_policy" = "Regole sull'utilizzo consentito"; "common_advanced_settings" = "Impostazioni avanzate"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Verification failed"; "common_verified" = "Verified"; "common_verify_device" = "Verifica dispositivo"; +"common_verify_identity" = "Verify identity"; "common_video" = "Video"; "common_voice_message" = "Messaggio vocale"; "common_waiting" = "In attesa…"; @@ -246,6 +247,8 @@ "common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; "common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Il backup della chat non è attualmente sincronizzato. Devi confermare la chiave di recupero per mantenere l'accesso al backup della chat."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "Inserisci la chiave di recupero"; "crash_detection_dialog_content" = "%1$@ si è chiuso inaspettatamente l'ultima volta che è stato usato. Vuoi condividere con noi un rapporto sull'arresto anomalo?"; "crypto_identity_change_pin_violation" = "L'identità di %1$@ sembra essere cambiata. %2$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "Rientro a sinistra"; "rich_text_editor_url_placeholder" = "Collegamento"; "rich_text_editor_a11y_add_attachment" = "Aggiungi allegato"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "screen_advanced_settings_element_call_base_url" = "URL base di Element Call personalizzato"; "screen_advanced_settings_element_call_base_url_description" = "Imposta un URL di base personalizzato per Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL non valido, assicurati di includere il protocollo (http/https) e l'indirizzo corretto."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Stai per creare un account su %@"; "screen_advanced_settings_developer_mode" = "Modalità sviluppatore"; "screen_advanced_settings_developer_mode_description" = "Attiva per avere accesso alle funzionalità per sviluppatori."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "Disattiva l'editor di testo avanzato per scrivere manualmente in Markdown"; "screen_advanced_settings_send_read_receipts" = "Ricevute di visualizzazione"; "screen_advanced_settings_send_read_receipts_description" = "Se disattivato, le tue ricevute di visualizzazione non verranno inviate a nessuno. Riceverai comunque ricevute di visualizzazione da altri utenti."; @@ -458,12 +462,12 @@ "screen_chat_backup_key_backup_action_enable" = "Attiva il backup"; "screen_chat_backup_key_backup_description" = "Il backup ti garantisce di non perdere la cronologia dei messaggi. %1$@."; "screen_chat_backup_key_backup_title" = "Backup"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; "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" = "Cambia la chiave di recupero"; "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" = "Il backup delle conversazioni non è attualmente sincronizzato."; -"screen_chat_backup_recovery_action_setup" = "Configura il recupero"; "screen_chat_backup_recovery_action_setup_description" = "Ottieni l'accesso ai tuoi messaggi cifrati se perdi tutti i tuoi dispositivi o se sei disconnesso da %1$@ ovunque."; "screen_create_account_title" = "Crea account"; "screen_create_new_recovery_key_list_item_1" = "Apri %1$@ in un dispositivo desktop"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Mostra meno"; "screen_room_timeline_message_copied" = "Messaggio copiato"; "screen_room_timeline_no_permission_to_post" = "Non sei autorizzato a postare in questa stanza"; -"screen_room_timeline_reactions_show_less" = "Mostra meno"; "screen_room_timeline_reactions_show_more" = "Mostra di più"; "screen_room_timeline_read_marker_title" = "Nuovo"; "screen_room_title" = "Conversazione"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "Confronta un set unico di emoji."; "screen_session_verification_request_accepted_subtitle" = "Confronta le emoji uniche, assicurandoti che appaiano nello stesso ordine."; "screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "Nessun distributore di notifiche push trovato."; "troubleshoot_notifications_test_unified_push_title" = "Controlla UnifiedPush"; "a11y_poll" = "Sondaggio"; +"banner_set_up_recovery_submit" = "Configura il recupero"; "dialog_title_error" = "Errore"; "dialog_title_success" = "Operazione riuscita"; "notification_fallback_content" = "Notifica"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Sblocca utente"; "screen_bug_report_rash_logs_alert_title" = "%1$@ si è chiuso inaspettatamente l'ultima volta che è stato usato. Vuoi condividere con noi un rapporto sull'arresto anomalo?"; "screen_chat_backup_recovery_action_confirm" = "Inserisci la chiave di recupero"; +"screen_chat_backup_recovery_action_setup" = "Configura il recupero"; "screen_create_poll_cancel_confirmation_content_ios" = "Le modifiche non verranno salvate"; "screen_create_room_add_people_title" = "Invita persone"; "screen_create_room_room_name_label" = "Nome stanza"; @@ -1029,7 +1033,7 @@ "screen_notification_settings_mentions_section_title" = "Menzioni"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Riprova"; "screen_recovery_key_change_generate_key_description" = "Assicurati di conservare la chiave di recupero in un posto sicuro"; -"screen_recovery_key_confirm_title" = "Inserisci la chiave di recupero"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Blocca utente"; "screen_reset_encryption_password_placeholder" = "Inserisci..."; "screen_room_attachment_source_camera_photo" = "Scatta foto"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Elaborazione del file multimediale da caricare fallita, riprova."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Rimuovi ed escludi"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Solo menzioni e parole chiave"; +"screen_room_timeline_reactions_show_less" = "Mostra meno"; "screen_roomlist_filter_people" = "Persone"; "screen_server_confirmation_change_server" = "Cambia fornitore dell'account"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "Disconnetti"; "screen_signout_confirmation_dialog_title" = "Disconnetti"; "screen_signout_key_backup_offline_title" = "Il backup delle chiavi è ancora in corso"; diff --git a/ElementX/Resources/Localizations/ka.lproj/Localizable.strings b/ElementX/Resources/Localizations/ka.lproj/Localizable.strings index 5ff6c8560a..33314f91c3 100644 --- a/ElementX/Resources/Localizations/ka.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ka.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; "banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; -"banner.set_up_recovery.title" = "Set up recovery"; +"banner_set_up_recovery_content" = "Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices."; +"banner_set_up_recovery_title" = "Set up recovery to protect your account"; "common_about" = "შესახებ"; "common_acceptable_use_policy" = "მისაღები გამოყენების პოლიტიკა"; "common_advanced_settings" = "გაფართოებული პარამეტრები"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Verification failed"; "common_verified" = "Verified"; "common_verify_device" = "დაადასტურეთ მოწყობილობა"; +"common_verify_identity" = "Verify identity"; "common_video" = "ვიდეო"; "common_voice_message" = "ხმოვანი შეტყობინება"; "common_waiting" = "მოცდა..."; @@ -246,6 +247,8 @@ "common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; "common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "თქვენი ჩეთების სარეზერვო ასლი ამჟამად არ არის სინქრონიზებული. თქვენ უნდა შეიყვანოთ თქვენი აღდგენის გასაღები, რათა შეინარჩუნოთ წვდომა ჩეთების სარეზერვო ასლზე."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "შეიყვანეთ აღდგენის გასაღები"; "crash_detection_dialog_content" = "%1$@ ავარიულად გაითიშა ბოლოს გამოიყენებისას. გსურთ, გამოგვიგზავნოთ ავარიული გათიშვის ჟურნალი?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "აბზაცის გარეშე"; "rich_text_editor_url_placeholder" = "Ბმული"; "rich_text_editor_a11y_add_attachment" = "დაამატეთ დანართი"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "screen_advanced_settings_element_call_base_url" = "მორგებული Element-ის ზარის საბაზისო URL"; "screen_advanced_settings_element_call_base_url_description" = "დააყენეთ საბაზისო URL Element-ის ზარებისათვის."; "screen_advanced_settings_element_call_base_url_validation_error" = "არასწორი URL, გთხოვთ, დარწმუნდეთ, რომ შეიტანეთ პროტოკოლი (http/https) და სწორი მისამართი."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "თქვენ აპირებთ ანგარიშის შექმნას %@-ში"; "screen_advanced_settings_developer_mode" = "დეველოპერის რეჟიმი"; "screen_advanced_settings_developer_mode_description" = "ჩართეთ დეველოპერების ფუნქციებზე წვდომა."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "გამორთეთ მდიდარი ტექსტის რედაქტორი, რათა ხელით აკრიფოთ Markdown."; "screen_advanced_settings_send_read_receipts" = "Read receipts"; "screen_advanced_settings_send_read_receipts_description" = "If turned off, your read receipts won't be sent to anyone. You will still receive read receipts from other users."; @@ -457,13 +461,13 @@ "screen_chat_backup_key_backup_action_disable" = "სარეზერვო ასლის გამორთვა"; "screen_chat_backup_key_backup_action_enable" = "სარეზერვო ასლის ჩართვა"; "screen_chat_backup_key_backup_description" = "სარეზერვო ასლი უზრუნველყოფს იმას, რომ თქვენ შეტყობინებების ისტორიას არ დაკარგავთ. %1$@"; -"screen_chat_backup_key_backup_title" = "სარეზერვო ასლი"; +"screen_chat_backup_key_backup_title" = "გასაღების საცავი"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; "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" = "აღდგენის გასაღების შეცვლა"; "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" = "თქვენი ჩატის სარეზერვო ასლი ამჟამად არ არის სინქრონიზებული."; -"screen_chat_backup_recovery_action_setup" = "აღდგენის დაყენება"; "screen_chat_backup_recovery_action_setup_description" = "მიიღეთ წვდომა თქვენს დაშიფრულ შეტყობინებებზე, თუ დაკარგავთ თქვენს ყველა მოწყობილობას ან გამოხვალთ სისტემიდან %1$@-დან ყველგან."; "screen_create_account_title" = "Create account"; "screen_create_new_recovery_key_list_item_1" = "Open %1$@ in a desktop device"; @@ -484,9 +488,9 @@ "screen_create_room_action_create_room" = "ახალი ოთახი"; "screen_create_room_error_creating_room" = "ოთახის შექმნისას შეცდომა მოხდა"; "screen_create_room_private_option_description" = "ამ ოთახში შეტყობინებები დაშიფრულია. შემდგომ დაშიფვრის გამორთვა შეუძლებელია."; -"screen_create_room_private_option_title" = "კერძო ოთახი (მხოლოდ მოწვევა)"; -"screen_create_room_public_option_description" = "შეტყობინებები არ არის დაშიფრული და ყველას შეუძლია მათი წაკითხვა. შეგიძლიათ ჩართოთ დაშიფვრა მოგვიანებით."; -"screen_create_room_public_option_title" = "საჯარო ოთახი (ნებისმიერი)"; +"screen_create_room_private_option_title" = "კერძო ოთახი"; +"screen_create_room_public_option_description" = "ყველას ამ ოთახის მოძებნა შეუძლია.\nთქვენ ნებისმიერ დროს შეგიძლიათ ამის შეცვლა ოთახის პარამეტრებში."; +"screen_create_room_public_option_title" = "საჯარო ოთახი"; "screen_create_room_topic_label" = "თემა (სურვილისამებრ)"; "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"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "ნაკლების ჩვენება"; "screen_room_timeline_message_copied" = "შეტყობინება დაკოპირდა"; "screen_room_timeline_no_permission_to_post" = "თქვენ არ გაქვთ ამ ოთახში გამოქვეყნების ნებართვა"; -"screen_room_timeline_reactions_show_less" = "ნაკლების ჩვენება"; "screen_room_timeline_reactions_show_more" = "მეტის ჩვენება"; "screen_room_timeline_read_marker_title" = "ახალი"; "screen_room_title" = "Chat"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "შეადარეთ ემოციების უნიკალური ნაკრები."; "screen_session_verification_request_accepted_subtitle" = "შეადარეთ უნიკალური ემოჯი, დარწმუნდით, რომ ისინი ერთი დ იმავე თანმიმდევრობით გამოჩნდნენ."; "screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "No push distributors found."; "troubleshoot_notifications_test_unified_push_title" = "Check UnifiedPush"; "a11y_poll" = "გამოკითხვა"; +"banner_set_up_recovery_submit" = "აღდგენის დაყენება"; "dialog_title_error" = "შეცდომა"; "dialog_title_success" = "წარმატება"; "notification_fallback_content" = "შეტყობინება"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Მომხმარებლის განბლოკვა"; "screen_bug_report_rash_logs_alert_title" = "%1$@ ავარიულად გაითიშა ბოლოს გამოიყენებისას. გსურთ, გამოგვიგზავნოთ ავარიული გათიშვის ჟურნალი?"; "screen_chat_backup_recovery_action_confirm" = "Enter recovery key"; +"screen_chat_backup_recovery_action_setup" = "აღდგენის დაყენება"; "screen_create_poll_cancel_confirmation_content_ios" = "Your changes won’t be saved"; "screen_create_room_add_people_title" = "ხალხის მოწვევა"; "screen_create_room_room_name_label" = "ოთახის სახელი"; @@ -1029,7 +1033,7 @@ "screen_notification_settings_mentions_section_title" = "ხსენებები"; "screen_qr_code_login_invalid_scan_state_retry_button" = "ხელახლა ცდა"; "screen_recovery_key_change_generate_key_description" = "დარწმუნდით, რომ შეგიძლიათ შეინახოთ თქვენი აღდგენის გასაღები სადმე უსაფრთხო ადგილას"; -"screen_recovery_key_confirm_title" = "შეიყვანეთ აღდგენის გასაღები"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "მომხმარებლის დაბლოკვა"; "screen_reset_encryption_password_placeholder" = "შეყვანა"; "screen_room_attachment_source_camera_photo" = "ფოტოს გადაღება"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "მედიის ატვირთვა ვერ მოხერხდა. გთხოვთ, სცადოთ ხელახლა."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Remove and ban member"; "screen_room_notification_settings_mode_mentions_and_keywords" = "მხოლოდ ხსენებები და საკვანძო სიტყვები"; +"screen_room_timeline_reactions_show_less" = "ნაკლების ჩვენება"; "screen_roomlist_filter_people" = "ხალხი"; "screen_server_confirmation_change_server" = "შეცვალეთ ანგარიშის მომწოდებელი"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "გამოსვლა"; "screen_signout_confirmation_dialog_title" = "გამოსვლა"; "screen_signout_key_backup_offline_title" = "თქვენი გასაღებების სარეზერვო ასლი ჯერ კიდევ შექმნის პროცესშია"; diff --git a/ElementX/Resources/Localizations/nl.lproj/Localizable.strings b/ElementX/Resources/Localizations/nl.lproj/Localizable.strings index 32363dc571..a149e82a7b 100644 --- a/ElementX/Resources/Localizations/nl.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/nl.lproj/Localizable.strings @@ -26,21 +26,21 @@ "action_back" = "Terug"; "action_call" = "Bellen"; "action_cancel" = "Annuleren"; -"action_cancel_for_now" = "Cancel for now"; +"action_cancel_for_now" = "Voor nu annuleren"; "action_choose_photo" = "Kies foto"; "action_clear" = "Wissen"; "action_close" = "Sluiten"; "action_complete_verification" = "Verificatie voltooien"; "action_confirm" = "Bevestigen"; -"action_confirm_password" = "Confirm password"; +"action_confirm_password" = "Bevestig wachtwoord"; "action_continue" = "Voortzetten"; "action_copy" = "Kopiëren"; "action_copy_link" = "Kopieer link"; "action_copy_link_to_message" = "Kopieer link naar bericht"; "action_create" = "Aanmaken"; "action_create_a_room" = "Creëer een kamer"; -"action_deactivate" = "Deactivate"; -"action_deactivate_account" = "Deactivate account"; +"action_deactivate" = "Sluiten"; +"action_deactivate_account" = "Account sluiten"; "action_decline" = "Weigeren"; "action_delete_poll" = "Peiling verwijderen"; "action_disable" = "Uitschakelen"; @@ -75,7 +75,7 @@ "action_ok" = "OK"; "action_open_settings" = "Instellingen"; "action_open_with" = "Openen met"; -"action_pin" = "Pin"; +"action_pin" = "Vastmaken"; "action_quick_reply" = "Snel antwoord"; "action_quote" = "Citeren"; "action_react" = "Reageren"; @@ -86,7 +86,7 @@ "action_report_bug" = "Probleem melden"; "action_report_content" = "Inhoud melden"; "action_reset" = "Opnieuw instellen"; -"action_reset_identity" = "Reset identity"; +"action_reset_identity" = "Identiteit opnieuw instellen"; "action_retry" = "Opnieuw proberen"; "action_retry_decryption" = "Decryptie opnieuw proberen"; "action_save" = "Opslaan"; @@ -95,7 +95,7 @@ "action_send_message" = "Bericht verzenden"; "action_share" = "Delen"; "action_share_link" = "Link delen"; -"action_show" = "Show"; +"action_show" = "Toon"; "action_sign_in_again" = "Log opnieuw in"; "action_signout" = "Uitloggen"; "action_signout_anyway" = "Toch uitloggen"; @@ -107,16 +107,16 @@ "action_take_photo" = "Foto maken"; "action_tap_for_options" = "Tik voor opties"; "action_try_again" = "Probeer het opnieuw"; -"action_unpin" = "Unpin"; -"action_view_in_timeline" = "View in timeline"; +"action_unpin" = "Losmaken"; +"action_view_in_timeline" = "Bekijk in tijdlijn"; "action_view_source" = "Bron weergeven"; "action_yes" = "Ja"; -"banner_migrate_to_native_sliding_sync_action" = "Log Out & Upgrade"; -"banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; -"banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; -"banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; -"banner.set_up_recovery.title" = "Set up recovery"; +"banner_migrate_to_native_sliding_sync_action" = "Uitloggen & Upgraden"; +"banner_migrate_to_native_sliding_sync_description" = "Je server ondersteunt nu een nieuw, sneller protocol. Log uit en log opnieuw in om nu te upgraden. Als je dit nu doet, voorkom je dat je geforceerd uitlogt wordt wanneer het oude protocol later wordt verwijderd."; +"banner_migrate_to_native_sliding_sync_force_logout_title" = "Je homeserver ondersteunt het oude protocol niet meer. Log uit en log opnieuw in om de app te blijven gebruiken."; +"banner_migrate_to_native_sliding_sync_title" = "Upgrade beschikbaar"; +"banner_set_up_recovery_content" = "Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices."; +"banner_set_up_recovery_title" = "Set up recovery to protect your account"; "common_about" = "Over"; "common_acceptable_use_policy" = "Beleid inzake redelijk gebruik"; "common_advanced_settings" = "Geavanceerde instellingen"; @@ -231,25 +231,28 @@ "common_verification_cancelled" = "Verificatie geannuleerd"; "common_verification_complete" = "Verificatie voltooid"; "common_verification_failed" = "Verification failed"; -"common_verified" = "Verified"; +"common_verified" = "Geverifieerd"; "common_verify_device" = "Apparaat verifiëren"; +"common_verify_identity" = "Verify identity"; "common_video" = "Video"; "common_voice_message" = "Spraakbericht"; "common_waiting" = "Wachten…"; "common_waiting_for_decryption_key" = "Wachten op dit bericht"; -"common.copied_to_clipboard" = "Copied to clipboard"; -"common.do_not_show_this_again" = "Do not show this again"; -"common.open_source_licenses" = "Open source licenses"; -"common.pinned" = "Pinned"; +"common.copied_to_clipboard" = "Gekopieerd naar klembord"; +"common.do_not_show_this_again" = "Dit niet meer weergeven"; +"common.open_source_licenses" = "Open-sourcelicenties"; +"common.pinned" = "Vastgezet"; "common.send_to" = "Sturen naar"; -"common.you" = "You"; +"common.you" = "Jij"; "common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; "common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Je chatback-up is momenteel niet gesynchroniseerd. Je moet je herstelsleutel invoeren om toegang te behouden tot je chatback-up."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "Voer je herstelsleutel in"; "crash_detection_dialog_content" = "%1$@ crashte de laatste keer dat het werd gebruikt. Wil je een crashrapport met ons delen?"; -"crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; -"crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; +"crypto_identity_change_pin_violation" = "%1$@'s identiteit lijkt te zijn veranderd. %2$@"; +"crypto_identity_change_pin_violation_new" = "%1$@'s %2$@ identiteit lijkt te zijn veranderd. %3$@"; "crypto_identity_change_pin_violation_new_user_id" = "(%1$@)"; "dialog_permission_camera" = "Geef toestemming in de systeeminstellingen om de applicatie de camera te laten gebruiken."; "dialog_permission_generic" = "Geef hiervoor toestemming in de systeeminstellingen."; @@ -271,7 +274,7 @@ "emoji_picker_category_people" = "Smileys & Mensen"; "emoji_picker_category_places" = "Reizen & Locaties"; "emoji_picker_category_symbols" = "Symbolen"; -"error_account_creation_not_possible" = "Your homeserver needs to be upgraded to support Matrix Authentication Service and account creation."; +"error_account_creation_not_possible" = "Je homeserver moet worden geüpgraded om de Matrix Authentication Service en het aanmaken van accounts te ondersteunen."; "error_failed_creating_the_permalink" = "Het aanmaken van de permanente link is mislukt"; "error_failed_loading_map" = "%1$@ kon de kaart niet laden. Probeer het later opnieuw."; "error_failed_loading_messages" = "Het laden van berichten is mislukt"; @@ -281,12 +284,12 @@ "error_no_compatible_app_found" = "Er is geen compatibele app gevonden om deze actie uit te voeren."; "error_some_messages_have_not_been_sent" = "Sommige berichten zijn niet verzonden"; "error_unknown" = "Sorry, er is een fout opgetreden"; -"event_shield_reason_authenticity_not_guaranteed" = "The authenticity of this encrypted message can't be guaranteed on this device."; -"event_shield_reason_previously_verified" = "Encrypted by a previously-verified user."; -"event_shield_reason_sent_in_clear" = "Not encrypted."; -"event_shield_reason_unknown_device" = "Encrypted by an unknown or deleted device."; -"event_shield_reason_unsigned_device" = "Encrypted by a device not verified by its owner."; -"event_shield_reason_unverified_identity" = "Encrypted by an unverified user."; +"event_shield_reason_authenticity_not_guaranteed" = "De echtheid van dit versleutelde bericht kan op dit apparaat niet worden gegarandeerd."; +"event_shield_reason_previously_verified" = "Versleuteld door een eerder geverifieerde gebruiker."; +"event_shield_reason_sent_in_clear" = "Niet versleuteld."; +"event_shield_reason_unknown_device" = "Versleuteld door een onbekend of verwijderd apparaat."; +"event_shield_reason_unsigned_device" = "Versleuteld door een apparaat dat niet is geverifieerd door de eigenaar."; +"event_shield_reason_unverified_identity" = "Versleuteld door een niet-geverifieerde gebruiker."; "full_screen_intent_banner_message" = "To ensure you never miss an important call, please change your settings to allow full-screen notifications when your phone is locked."; "full_screen_intent_banner_title" = "Verbeter je gesprekservaring"; "invite_friends_rich_title" = "🔐️ Sluit je bij mij aan op %1$@"; @@ -304,14 +307,14 @@ "notification_incoming_call" = "Inkomende oproep"; "notification_inline_reply_failed" = "** Verzenden is mislukt - open de kamer"; "notification_invite_body" = "Nodigde je uit om te chatten"; -"notification_invite_body_with_sender" = "%1$@ invited you to chat"; +"notification_invite_body_with_sender" = "%1$@ nodigde je uit om te chatten"; "notification_mentioned_you_body" = "Heeft je genoemd: %1$@"; "notification_new_messages" = "Nieuwe berichten"; "notification_reaction_body" = "Reageerde met %1$@"; "notification_room_invite_body" = "Nodigde je uit om tot de kamer toe te treden"; -"notification_room_invite_body_with_sender" = "%1$@ invited you to join the room"; +"notification_room_invite_body_with_sender" = "%1$@ nodigde je uit om tot de kamer toe te treden"; "notification_sender_me" = "Mij"; -"notification_sender_mention_reply" = "%1$@ mentioned or replied"; +"notification_sender_mention_reply" = "%1$@ heeft vermeld of beantwoord"; "notification_test_push_notification_content" = "Je bekijkt de melding! Klik hier!"; "notification_ticker_text_dm" = "%1$@: %2$@"; "notification_ticker_text_group" = "%1$@: %2$@ %3$@"; @@ -341,45 +344,46 @@ "rich_text_editor_unindent" = "Inspringing ongedaan maken"; "rich_text_editor_url_placeholder" = "Link"; "rich_text_editor_a11y_add_attachment" = "Bijlage toevoegen"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "screen_advanced_settings_element_call_base_url" = "Aangepaste basis-URL voor Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Stel een aangepaste basis-URL in voor Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Ongeldige URL, zorg ervoor dat je het protocol (http/https) en het juiste adres invult."; "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"; -"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; -"screen_create_room_access_section_knocking_option_title" = "Ask to join"; +"screen_create_room_access_section_anyone_option_description" = "Iedereen kan toetreden tot deze kamer"; +"screen_create_room_access_section_anyone_option_title" = "Iedereen"; +"screen_create_room_access_section_header" = "Toegang tot de kamer"; +"screen_create_room_access_section_knocking_option_description" = "Iedereen kan vragen om toe te treden tot de kamer, maar een beheerder of moderator moet het verzoek accepteren"; +"screen_create_room_access_section_knocking_option_title" = "Vraag om toe te treden"; "screen_join_room_cancel_knock_action" = "Cancel request"; "screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; "screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; "screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; -"screen_join_room_knock_message_description" = "Message (optional)"; +"screen_join_room_knock_message_description" = "Bericht (optioneel)"; "screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; -"screen_join_room_knock_sent_title" = "Request to join sent"; -"screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here."; -"screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered"; -"screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again."; -"screen_resolve_send_failure_changed_identity_primary_button_title" = "Withdraw verification and send"; -"screen_resolve_send_failure_changed_identity_subtitle" = "You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$@."; -"screen_resolve_send_failure_changed_identity_title" = "Your message was not sent because %1$@’s verified identity has changed"; -"screen_resolve_send_failure_unsigned_device_primary_button_title" = "Send message anyway"; -"screen_resolve_send_failure_unsigned_device_subtitle" = "%1$@ is using one or more unverified devices. You can send the message anyway, or you can cancel for now and try again later after %2$@ has verified all their devices."; -"screen_resolve_send_failure_unsigned_device_title" = "Your message was not sent because %1$@ has not verified all devices"; -"screen_resolve_send_failure_you_unsigned_device_subtitle" = "One or more of your devices are unverified. You can send the message anyway, or you can cancel for now and try again later after you have verified all of your devices."; -"screen_resolve_send_failure_you_unsigned_device_title" = "Your message was not sent because you have not verified one or more of your devices"; +"screen_join_room_knock_sent_title" = "Verzoek om toe te treden verzonden"; +"screen_pinned_timeline_empty_state_description" = "Druk op een bericht en kies „%1$@” om het hier toe te voegen."; +"screen_pinned_timeline_empty_state_headline" = "Zet belangrijke berichten vast zodat ze gemakkelijk te vinden zijn"; +"screen_reset_encryption_password_error" = "Er is een onbekende fout opgetreden. Controleer of het wachtwoord van je account juist is en probeer het opnieuw."; +"screen_resolve_send_failure_changed_identity_primary_button_title" = "Verificatie intrekken en verzenden"; +"screen_resolve_send_failure_changed_identity_subtitle" = "Je kunt je verificatie intrekken en dit bericht toch verzenden, of je kunt het voorlopig annuleren en het later opnieuw proberen nadat je %1$@ opnieuw hebt geverifieerd."; +"screen_resolve_send_failure_changed_identity_title" = "Je bericht is niet verzonden omdat %1$@'s geverifieerde identiteit is gewijzigd"; +"screen_resolve_send_failure_unsigned_device_primary_button_title" = "Bericht toch versturen"; +"screen_resolve_send_failure_unsigned_device_subtitle" = "%1$@ gebruikt een of meer niet-geverifieerde apparaten. Je kunt het bericht toch verzenden, of je kunt het voorlopig annuleren en het later opnieuw proberen nadat %2$@ alle apparaten heeft geverifieerd."; +"screen_resolve_send_failure_unsigned_device_title" = "Je bericht is niet verzonden omdat %1$@ niet alle apparaten heeft geverifieerd"; +"screen_resolve_send_failure_you_unsigned_device_subtitle" = "Een of meer van je apparaten zijn niet geverifieerd. Je kunt het bericht toch verzenden, of je kunt het voorlopig annuleren en het later opnieuw proberen nadat je al je apparaten hebt geverifieerd."; +"screen_resolve_send_failure_you_unsigned_device_title" = "Je bericht is niet verzonden omdat je een of meerdere apparaten niet geverifieerd hebt"; "screen_room_mentions_at_room_subtitle" = "Stuur een melding naar de hele kamer"; -"screen_room_pinned_banner_indicator" = "%1$@ of %2$@"; -"screen_room_pinned_banner_indicator_description" = "%1$@ Pinned messages"; -"screen_room_pinned_banner_loading_description" = "Loading message…"; -"screen_room_pinned_banner_view_all_button_title" = "View All"; -"screen_room_details_pinned_events_row_title" = "Pinned messages"; +"screen_room_pinned_banner_indicator" = "%1$@ van %2$@"; +"screen_room_pinned_banner_indicator_description" = "%1$@ Vastgezette berichten"; +"screen_room_pinned_banner_loading_description" = "Bericht laden..."; +"screen_room_pinned_banner_view_all_button_title" = "Bekijk alles"; +"screen_room_details_pinned_events_row_title" = "Vastgezette berichten"; "screen_roomlist_knock_event_sent_description" = "Request to join sent"; -"screen_timeline_item_menu_send_failure_changed_identity" = "Message not sent because %1$@’s verified identity has changed."; -"screen_timeline_item_menu_send_failure_unsigned_device" = "Message not sent because %1$@ has not verified all devices."; -"screen_timeline_item_menu_send_failure_you_unsigned_device" = "Message not sent because you have not verified one or more of your devices."; +"screen_timeline_item_menu_send_failure_changed_identity" = "Bericht niet verzonden omdat %1$@'s geverifieerde identiteit is gewijzigd."; +"screen_timeline_item_menu_send_failure_unsigned_device" = "Bericht niet verzonden omdat %1$@ niet alle apparaten heeft geverifieerd."; +"screen_timeline_item_menu_send_failure_you_unsigned_device" = "Bericht is niet verzonden omdat je een of meerdere apparaten niet geverifieerd hebt"; "screen_account_provider_form_hint" = "Homeserver-adres"; "screen_account_provider_form_notice" = "Voer een zoekterm of een domeinnaam in."; "screen_account_provider_form_subtitle" = "Zoek naar een bedrijf, community of privéserver."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Je staat op het punt een account aan te maken op %@"; "screen_advanced_settings_developer_mode" = "Ontwikkelaarsmodus"; "screen_advanced_settings_developer_mode_description" = "Schakel in om toegang te krijgen tot tools en functies voor ontwikkelaars."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "Schakel de uitgebreide tekstverwerker uit om Markdown handmatig te typen."; "screen_advanced_settings_send_read_receipts" = "Leesbevestigingen"; "screen_advanced_settings_send_read_receipts_description" = "Indien uitgeschakeld worden er geen leesbevestigingen verstuurd. Je ontvangt nog steeds leesbevestigingen van andere gebruikers."; @@ -456,16 +460,16 @@ "screen_change_server_title" = "Selecteer je server"; "screen_chat_backup_key_backup_action_disable" = "Back-up uitschakelen"; "screen_chat_backup_key_backup_action_enable" = "Back-up inschakelen"; -"screen_chat_backup_key_backup_description" = "Een back-up maken zorgt ervoor dat je je berichtgeschiedenis niet verliest. %1$@."; +"screen_chat_backup_key_backup_description" = "Sla je cryptografische identiteit en berichtsleutels veilig op de server op. Zo kun je je berichtgeschiedenis bekijken op nieuwe apparaten. %1$@."; "screen_chat_backup_key_backup_title" = "Back-up"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; "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" = "Herstelsleutel wijzigen"; "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" = "Je chatback-up is momenteel niet gesynchroniseerd."; -"screen_chat_backup_recovery_action_setup" = "Herstelmogelijkheid instellen"; "screen_chat_backup_recovery_action_setup_description" = "Krijg toegang tot je versleutelde berichten als je al je apparaten kwijtraakt of overal uit %1$@ bent uitgelogd."; -"screen_create_account_title" = "Create account"; +"screen_create_account_title" = "Account aanmaken"; "screen_create_new_recovery_key_list_item_1" = "Open %1$@ op een desktopapparaat"; "screen_create_new_recovery_key_list_item_2" = "Log opnieuw in op je account"; "screen_create_new_recovery_key_list_item_3" = "Wanneer je wordt gevraagd om je apparaat te verifiëren, selecteer %1$@"; @@ -484,21 +488,21 @@ "screen_create_room_action_create_room" = "Nieuwe kamer"; "screen_create_room_error_creating_room" = "Er is een fout opgetreden bij het aanmaken van de kamer"; "screen_create_room_private_option_description" = "Berichten in deze kamer zijn versleuteld. Versleuteling kan achteraf niet worden uitgeschakeld."; -"screen_create_room_private_option_title" = "Privé kamer (alleen op uitnodiging)"; -"screen_create_room_public_option_description" = "Berichten zijn niet versleuteld en iedereen kan ze lezen. Je kunt versleuteling later inschakelen."; -"screen_create_room_public_option_title" = "Openbare kamer (iedereen)"; +"screen_create_room_private_option_title" = "Privé kamer"; +"screen_create_room_public_option_description" = "Iedereen kan deze kamer vinden.\nJe kunt dit op elk gewenst moment wijzigen in de kamerinstellingen."; +"screen_create_room_public_option_title" = "Openbare kamer"; "screen_create_room_topic_label" = "Onderwerp (optioneel)"; -"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"; -"screen_deactivate_account_delete_all_messages_notice" = "Warning: Future users may see incomplete conversations."; -"screen_deactivate_account_description" = "Deactivating your account is %1$@, it will:"; -"screen_deactivate_account_description_bold_part" = "irreversible"; -"screen_deactivate_account_list_item_1" = "%1$@ your account (you can't log back in, and your ID can't be reused)."; -"screen_deactivate_account_list_item_1_bold_part" = "Permanently disable"; -"screen_deactivate_account_list_item_2" = "Remove you from all chat rooms."; -"screen_deactivate_account_list_item_3" = "Delete your account information from our identity server."; -"screen_deactivate_account_list_item_4" = "Your messages will still be visible to registered users but won’t be available to new or unregistered users if you choose to delete them."; -"screen_deactivate_account_title" = "Deactivate account"; +"screen_deactivate_account_confirmation_dialog_content" = "Bevestig dat je je account wilt sluiten. Deze actie kan niet ongedaan worden gemaakt."; +"screen_deactivate_account_delete_all_messages" = "Verwijder al mijn berichten"; +"screen_deactivate_account_delete_all_messages_notice" = "Waarschuwing: Toekomstige gebruikers kunnen onvolledige gesprekken te zien krijgen."; +"screen_deactivate_account_description" = "Je account sluiten is %1$@, het zal:"; +"screen_deactivate_account_description_bold_part" = "onomkeerbaar"; +"screen_deactivate_account_list_item_1" = "Je account %1$@ (je kunt niet opnieuw inloggen en je ID kan niet opnieuw worden gebruikt)"; +"screen_deactivate_account_list_item_1_bold_part" = "permanent uitschakelen"; +"screen_deactivate_account_list_item_2" = "Je verwijderen uit alle chatrooms."; +"screen_deactivate_account_list_item_3" = "Je accountgegevens verwijderen van onze identiteitsserver."; +"screen_deactivate_account_list_item_4" = "Je berichten zijn nog steeds zichtbaar voor geregistreerde gebruikers, maar niet beschikbaar voor nieuwe of niet-geregistreerde gebruikers als je ervoor kiest ze te verwijderen."; +"screen_deactivate_account_title" = "Account sluiten"; "screen_edit_poll_delete_confirmation" = "Weet je zeker dat je deze peiling wilt verwijderen?"; "screen_edit_profile_display_name" = "Weergavenaam"; "screen_edit_profile_display_name_placeholder" = "Je weergavenaam"; @@ -506,18 +510,18 @@ "screen_edit_profile_error_title" = "Kan profiel niet bijwerken"; "screen_edit_profile_title" = "Profiel bewerken"; "screen_edit_profile_updating_details" = "Profiel bijwerken…"; -"screen_encryption_reset_action_continue_reset" = "Continue reset"; -"screen_encryption_reset_bullet_1" = "Your account details, contacts, preferences, and chat list will be kept"; -"screen_encryption_reset_bullet_2" = "You will lose any message history that’s stored only on the server"; -"screen_encryption_reset_bullet_3" = "You will need to verify all your existing devices and contacts again"; -"screen_encryption_reset_footer" = "Only reset your identity if you don’t have access to another signed-in device and you’ve lost your recovery key."; -"screen_encryption_reset_title" = "Can't confirm? You’ll need to reset your identity."; -"screen_identity_confirmation_cannot_confirm" = "Can't confirm?"; +"screen_encryption_reset_action_continue_reset" = "Doorgaan met opnieuw instellen"; +"screen_encryption_reset_bullet_1" = "Je accountgegevens, contacten, voorkeuren en chatlijst worden bewaard"; +"screen_encryption_reset_bullet_2" = "Je verliest alle berichtgeschiedenis die alleen op de server is opgeslagen"; +"screen_encryption_reset_bullet_3" = "Je moet al je bestaande apparaten en contacten opnieuw verifiëren"; +"screen_encryption_reset_footer" = "Stel je identiteit alleen opnieuw in als je geen toegang hebt tot een ander aangemeld apparaat en je je herstelsleutel kwijt bent."; +"screen_encryption_reset_title" = "Kun je dit niet bevestigen? Je zult je identiteit opnieuw moeten instellen."; +"screen_identity_confirmation_cannot_confirm" = "Kan ik dit niet bevestigen?"; "screen_identity_confirmation_create_new_recovery_key" = "Maak een nieuwe herstelsleutel"; "screen_identity_confirmation_subtitle" = "Verifieer dit apparaat om beveiligde berichten in te stellen."; "screen_identity_confirmation_title" = "Bevestig dat jij het bent"; -"screen_identity_confirmation_use_another_device" = "Use another device"; -"screen_identity_confirmation_use_recovery_key" = "Use recovery key"; +"screen_identity_confirmation_use_another_device" = "Gebruik een ander apparaat"; +"screen_identity_confirmation_use_recovery_key" = "Gebruik de herstelsleutel"; "screen_identity_confirmed_subtitle" = "Nu kun je veilig berichten lezen of verzenden, en iedereen met wie je chat kan dit apparaat ook vertrouwen."; "screen_identity_confirmed_title" = "Apparaat geverifieerd"; "screen_identity_waiting_on_other_device" = "Wachten op ander apparaat..."; @@ -542,7 +546,7 @@ "screen_key_backup_disable_description_point_1" = "Geen berichtgeschiedenis hebben van versleutelde berichten op nieuwe apparaten"; "screen_key_backup_disable_description_point_2" = "Toegang verliezen tot je versleutelde berichten als je overal uit %1$@ bent uitgelogd."; "screen_key_backup_disable_title" = "Weet je zeker dat je de back-up wilt uitschakelen?"; -"screen_login_error_deactivated_account" = "Dit account is gedeactiveerd."; +"screen_login_error_deactivated_account" = "Dit account is gesloten."; "screen_login_error_invalid_credentials" = "Onjuiste gebruikersnaam en/of wachtwoord"; "screen_login_error_invalid_user_id" = "Dit is geen geldige gebruikers-ID. Verwacht formaat: '@user:homeserver.org'"; "screen_login_error_refresh_tokens" = "Deze server is geconfigureerd om verversingstokens te gebruiken. Deze worden niet ondersteund bij inloggen met een wachtwoord."; @@ -619,7 +623,7 @@ "screen_qr_code_login_initial_state_item_3" = "Selecteer %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Nieuw apparaat koppelen”"; "screen_qr_code_login_initial_state_item_4" = "Scan de QR-code met dit apparaat"; -"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; +"screen_qr_code_login_initial_state_subtitle" = "Alleen beschikbaar als je accountprovider dit ondersteunt."; "screen_qr_code_login_initial_state_title" = "Open %1$@ op een ander apparaat om de QR-code te krijgen"; "screen_qr_code_login_invalid_scan_state_description" = "Gebruik de QR-code die op het andere apparaat wordt weergegeven."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Verkeerde QR-code"; @@ -647,26 +651,26 @@ "screen_recovery_key_copied_to_clipboard" = "Herstelsleutel gekopieerd"; "screen_recovery_key_generating_key" = "Genereren..."; "screen_recovery_key_save_action" = "Herstelsleutel opslaan"; -"screen_recovery_key_save_description" = "Noteer je herstelsleutel op een veilige plek of bewaar deze in een wachtwoordmanager."; +"screen_recovery_key_save_description" = "Bewaar je herstelsleutel op een veilige plek, zoals in een wachtwoordbeheerder, een versleutelde notitie of in een fysieke kluis."; "screen_recovery_key_save_key_description" = "Tik om de herstelsleutel te kopiëren"; "screen_recovery_key_save_title" = "Sla je herstelsleutel op"; "screen_recovery_key_setup_confirmation_description" = "Na deze stap kun je je nieuwe herstelsleutel niet meer inzien."; "screen_recovery_key_setup_confirmation_title" = "Heb je je herstelsleutel opgeslagen?"; "screen_recovery_key_setup_description" = "Je chatback-up wordt beschermd door een herstelsleutel. Als je na de installatie een nieuwe herstelsleutel nodig hebt, kun je deze opnieuw aanmaken door 'Herstelsleutel wijzigen' te selecteren."; "screen_recovery_key_setup_generate_key" = "Genereer je herstelsleutel"; -"screen_recovery_key_setup_generate_key_description" = "Zorg ervoor dat je je herstelsleutel op een veilige plek kunt bewaren"; +"screen_recovery_key_setup_generate_key_description" = "Deel dit met niemand!"; "screen_recovery_key_setup_success" = "Herstelmogelijkheid succesvol ingesteld"; "screen_recovery_key_setup_title" = "Herstelmogelijkheid instellen"; "screen_report_content_block_user_hint" = "Vink aan als je alle huidige en toekomstige berichten van deze gebruiker wilt verbergen"; "screen_report_content_explanation" = "Dit bericht wordt gerapporteerd aan de beheerder van je homeserver. Ze zullen geen versleutelde berichten kunnen lezen."; "screen_report_content_hint" = "Reden voor het melden van deze inhoud"; -"screen_reset_encryption_confirmation_alert_action" = "Yes, reset now"; -"screen_reset_encryption_confirmation_alert_subtitle" = "This process is irreversible."; -"screen_reset_encryption_confirmation_alert_title" = "Are you sure you want to reset your identity?"; -"screen_reset_encryption_password_subtitle" = "Confirm that you want to reset your identity."; -"screen_reset_encryption_password_title" = "Enter your account password to continue"; -"screen_reset_identity_confirmation_subtitle" = "You're about to go to your %1$@ account to reset your identity. Afterwards you'll be taken back to the app."; -"screen_reset_identity_confirmation_title" = "Can't confirm? Go to your account to reset your identity."; +"screen_reset_encryption_confirmation_alert_action" = "Ja, nu opnieuw instellen"; +"screen_reset_encryption_confirmation_alert_subtitle" = "Dit proces is onomkeerbaar."; +"screen_reset_encryption_confirmation_alert_title" = "Weet je zeker dat je je identiteit opnieuw wilt instellen?"; +"screen_reset_encryption_password_subtitle" = "Bevestig dat je je identiteit opnieuw wilt instellen."; +"screen_reset_encryption_password_title" = "Voer het wachtwoord van je account in om verder te gaan"; +"screen_reset_identity_confirmation_subtitle" = "Je staat op het punt naar je %1$@ account te gaan om je identiteit opnieuw in te stellen. Daarna kom je terug naar de app."; +"screen_reset_identity_confirmation_title" = "Kun je dit niet bevestigen? Ga naar je account om je identiteit opnieuw in te stellen."; "screen_room_alias_resolver_resolve_alias_failure" = "Kan het kameradres niet vinden."; "screen_room_attachment_source_camera" = "Camera"; "screen_room_attachment_source_camera_video" = "Video opnemen"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Toon minder"; "screen_room_timeline_message_copied" = "Bericht gekopieerd"; "screen_room_timeline_no_permission_to_post" = "Je hebt geen toestemming om berichten in deze kamer te plaatsen"; -"screen_room_timeline_reactions_show_less" = "Minder tonen"; "screen_room_timeline_reactions_show_more" = "Meer tonen"; "screen_room_timeline_read_marker_title" = "Nieuw"; "screen_room_title" = "Chat"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "Vergelijk een unieke combinatie van emoji's."; "screen_session_verification_request_accepted_subtitle" = "Vergelijk de unieke emoji's, ze dienen in dezelfde volgorde te worden weergegeven."; "screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; @@ -926,12 +928,12 @@ "state_event_room_name_removed_by_you" = "Je hebt de kamernaam verwijderd"; "state_event_room_none" = "%1$@ heeft geen wijzigingen aangebracht"; "state_event_room_none_by_you" = "Je hebt geen wijzigingen aangebracht"; -"state_event_room_pinned_events_changed" = "%1$@ changed the pinned messages"; -"state_event_room_pinned_events_changed_by_you" = "You changed the pinned messages"; -"state_event_room_pinned_events_pinned" = "%1$@ pinned a message"; -"state_event_room_pinned_events_pinned_by_you" = "You pinned a message"; -"state_event_room_pinned_events_unpinned" = "%1$@ unpinned a message"; -"state_event_room_pinned_events_unpinned_by_you" = "You unpinned a message"; +"state_event_room_pinned_events_changed" = "%1$@ heeft de vastgezette berichten gewijzigd"; +"state_event_room_pinned_events_changed_by_you" = "Je hebt de vastgezette berichten gewijzigd"; +"state_event_room_pinned_events_pinned" = "%1$@ heeft een bericht vastgezet"; +"state_event_room_pinned_events_pinned_by_you" = "Je hebt een bericht vastgezet"; +"state_event_room_pinned_events_unpinned" = "%1$@ heeft een bericht losgemaakt"; +"state_event_room_pinned_events_unpinned_by_you" = "Je hebt een bericht losgemaakt"; "state_event_room_reject" = "%1$@ heeft de uitnodiging afgewezen"; "state_event_room_reject_by_you" = "Je hebt de uitnodiging afgewezen"; "state_event_room_remove" = "%1$@ heeft %2$@ verwijderd"; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "Geen push-verdelers gevonden."; "troubleshoot_notifications_test_unified_push_title" = "UnifiedPush controleren"; "a11y_poll" = "Peiling"; +"banner_set_up_recovery_submit" = "Herstelmogelijkheid instellen"; "dialog_title_error" = "Fout"; "dialog_title_success" = "Geslaagd"; "notification_fallback_content" = "Melding"; @@ -999,7 +1002,7 @@ "notification_invitation_action_reject" = "Weiger"; "notification_room_action_mark_as_read" = "Markeren als gelezen"; "notification_room_action_quick_reply" = "Snel antwoord"; -"screen_pinned_timeline_screen_title_empty" = "Pinned messages"; +"screen_pinned_timeline_screen_title_empty" = "Vastgezette berichten"; "screen_room_mentions_at_room_title" = "Iedereen"; "screen_account_provider_change" = "Wijzig accountprovider"; "screen_account_provider_signin_subtitle" = "Dit is waar je gesprekken zullen worden bewaard — net zoals je een e-mailprovider zou gebruiken om je e-mails te bewaren."; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Gebruiker deblokkeren"; "screen_bug_report_rash_logs_alert_title" = "%1$@ crashte de laatste keer dat het werd gebruikt. Wil je een crashrapport met ons delen?"; "screen_chat_backup_recovery_action_confirm" = "Voer herstelsleutel in"; +"screen_chat_backup_recovery_action_setup" = "Herstelmogelijkheid instellen"; "screen_create_poll_cancel_confirmation_content_ios" = "Je wijzigingen worden niet opgeslagen"; "screen_create_room_add_people_title" = "Mensen uitnodigen"; "screen_create_room_room_name_label" = "Naam van de kamer"; @@ -1024,12 +1028,12 @@ "screen_dm_details_unblock_user" = "Gebruiker deblokkeren"; "screen_edit_poll_delete_confirmation_title" = "Peiling verwijderen"; "screen_edit_poll_title" = "Peiling wijzigen"; -"screen_identity_use_another_device" = "Use another device"; +"screen_identity_use_another_device" = "Gebruik een ander apparaat"; "screen_login_subtitle" = "Matrix is een open netwerk voor veilige, gedecentraliseerde communicatie."; "screen_notification_settings_mentions_section_title" = "Vermeldingen"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Probeer het opnieuw"; -"screen_recovery_key_change_generate_key_description" = "Zorg ervoor dat je je herstelsleutel op een veilige plek kunt bewaren"; -"screen_recovery_key_confirm_title" = "Voer je herstelsleutel in"; +"screen_recovery_key_change_generate_key_description" = "Deel dit met niemand!"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Gebruiker blokkeren"; "screen_reset_encryption_password_placeholder" = "Voer in..."; "screen_room_attachment_source_camera_photo" = "Foto maken"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Het verwerken van media voor uploaden is mislukt. Probeer het opnieuw."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Lid verwijderen en verbannen"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Alleen vermeldingen en trefwoorden"; +"screen_room_timeline_reactions_show_less" = "Toon minder"; "screen_roomlist_filter_people" = "Personen"; "screen_server_confirmation_change_server" = "Wijzig accountprovider"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "Uitloggen"; "screen_signout_confirmation_dialog_title" = "Uitloggen"; "screen_signout_key_backup_offline_title" = "De backup van je sleutels is nog bezig"; diff --git a/ElementX/Resources/Localizations/nl.lproj/Localizable.stringsdict b/ElementX/Resources/Localizations/nl.lproj/Localizable.stringsdict index d02360d9f9..a4b7f469a6 100644 --- a/ElementX/Resources/Localizations/nl.lproj/Localizable.stringsdict +++ b/ElementX/Resources/Localizations/nl.lproj/Localizable.stringsdict @@ -205,9 +205,9 @@ NSStringFormatValueTypeKey d one - %1$d Pinned message + %1$d Vastgezet bericht other - %1$d Pinned messages + %1$d Vastgezette berichten screen_room_member_list_header_title diff --git a/ElementX/Resources/Localizations/pl.lproj/Localizable.strings b/ElementX/Resources/Localizations/pl.lproj/Localizable.strings index a705326a60..56841a16e6 100644 --- a/ElementX/Resources/Localizations/pl.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pl.lproj/Localizable.strings @@ -54,7 +54,7 @@ "action_forgot_password" = "Nie pamiętasz hasła?"; "action_forward" = "Przekaż dalej"; "action_go_back" = "Wróć"; -"action_ignore" = "Ignore"; +"action_ignore" = "Ignoruj"; "action_invite" = "Zaproś"; "action_invite_friends" = "Zaproś znajomych"; "action_invite_friends_to_app" = "Zaproś znajomych do %1$@"; @@ -95,7 +95,7 @@ "action_send_message" = "Wyślij wiadomość"; "action_share" = "Udostępnij"; "action_share_link" = "Udostępnij link"; -"action_show" = "Show"; +"action_show" = "Pokaż"; "action_sign_in_again" = "Zaloguj się ponownie"; "action_signout" = "Wyloguj"; "action_signout_anyway" = "Wyloguj mimo to"; @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Twój serwer obsługuje teraz nowy, szybszy protokół. Wyloguj się i zaloguj ponownie, aby uaktualnić teraz. Zrobienie tego teraz pomoże uniknąć wymuszonego wylogowania, gdy stary protokół zostanie później usunięty."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Twój serwer domowy już nie wspiera starego protokołu. Zaloguj się ponownie, aby kontynuować korzystanie z aplikacji."; "banner_migrate_to_native_sliding_sync_title" = "Dostępna aktualizacja"; -"banner.set_up_recovery.content" = "Wygeneruj nowy klucz przywracania, którego można użyć do przywrócenia historii wiadomości szyfrowanych w przypadku utraty dostępu do swoich urządzeń."; -"banner.set_up_recovery.title" = "Skonfiguruj przywracanie"; +"banner_set_up_recovery_content" = "Wygeneruj nowy klucz przywracania, którego można użyć do przywrócenia historii wiadomości szyfrowanych w przypadku utraty dostępu do swoich urządzeń."; +"banner_set_up_recovery_title" = "Skonfiguruj przywracanie"; "common_about" = "O programie"; "common_acceptable_use_policy" = "Polityka użytkowania"; "common_advanced_settings" = "Ustawienia zaawansowane"; @@ -134,12 +134,12 @@ "common_dark" = "Ciemny"; "common_decryption_error" = "Błąd deszyfrowania"; "common_developer_options" = "Opcje programisty"; -"common_device_id" = "Device ID"; +"common_device_id" = "ID urządzenia"; "common_direct_chat" = "Czat prywatny"; "common_edited_suffix" = "(edytowane)"; "common_editing" = "Edytowanie"; "common_emote" = "* %1$@ %2$@"; -"common_encryption" = "Encryption"; +"common_encryption" = "Szyfrowanie"; "common_encryption_enabled" = "Szyfrowanie włączone"; "common_enter_your_pin" = "Wprowadź kod PIN"; "common_error" = "Błąd"; @@ -230,9 +230,10 @@ "common_username" = "Nazwa użytkownika"; "common_verification_cancelled" = "Weryfikacja anulowana"; "common_verification_complete" = "Weryfikacja zakończona"; -"common_verification_failed" = "Verification failed"; -"common_verified" = "Verified"; +"common_verification_failed" = "Weryfikacja nie powiodła się"; +"common_verified" = "Zweryfikowano"; "common_verify_device" = "Weryfikuj urządzenie"; +"common_verify_identity" = "Verify identity"; "common_video" = "Film"; "common_voice_message" = "Wiadomość głosowa"; "common_waiting" = "Oczekiwanie…"; @@ -243,9 +244,11 @@ "common.pinned" = "Przypięte"; "common.send_to" = "Wyślij do"; "common.you" = "Ty"; -"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; -"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; +"common_unable_to_decrypt_insecure_device" = "Wysłane z niebezpiecznego urządzenia"; +"common_unable_to_decrypt_verification_violation" = "Zweryfikowana tożsamość nadawcy uległa zmianie"; "confirm_recovery_key_banner_message" = "Twoja kopia zapasowa czatu jest obecnie niezsynchronizowana. Aby zachować dostęp do kopii zapasowej czatu, musisz potwierdzić klucz odzyskiwania."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "Wprowadź swój klucz przywracania"; "crash_detection_dialog_content" = "%1$@ uległ awarii podczas ostatniego użycia. Czy chcesz przesłać nam raport o awarii?"; "crypto_identity_change_pin_violation" = "Tożsamość %1$@ mogła ulec zmianie. %2$@"; @@ -341,24 +344,25 @@ "rich_text_editor_unindent" = "Bez wcięcia"; "rich_text_editor_url_placeholder" = "Link"; "rich_text_editor_a11y_add_attachment" = "Dodaj załącznik"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "screen_advanced_settings_element_call_base_url" = "Własny bazowy URL dla połączeń Element"; "screen_advanced_settings_element_call_base_url_description" = "Ustaw własny bazowy URL dla połączeń Element"; "screen_advanced_settings_element_call_base_url_validation_error" = "Nieprawidłowy adres URL, upewnij się, że zawiera protokół (http/https) i poprawny adres."; -"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"; -"screen_create_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request"; -"screen_create_room_access_section_knocking_option_title" = "Ask to join"; -"screen_join_room_cancel_knock_action" = "Cancel request"; -"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; -"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; -"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; -"screen_join_room_knock_message_description" = "Message (optional)"; -"screen_join_room_knock_sent_description" = "You will receive an invite to join the room if your request is accepted."; -"screen_join_room_knock_sent_title" = "Request to join sent"; +"screen_create_room_room_address_section_footer" = "Aby ten pokój był widoczny w katalogu pomieszczeń publicznych, będziesz potrzebował adres pokoju."; +"screen_create_room_room_address_section_title" = "Adres pokoju"; +"screen_create_room_room_visibility_section_title" = "Widoczność pomieszczenia"; +"screen_create_room_access_section_anyone_option_description" = "Każdy może dołączyć do tego pokoju"; +"screen_create_room_access_section_anyone_option_title" = "Wszyscy"; +"screen_create_room_access_section_header" = "Dostęp do pokoju"; +"screen_create_room_access_section_knocking_option_description" = "Każdy może poprosić o dołączenie do pokoju, ale administrator lub moderator będzie musiał zatwierdzić prośbę"; +"screen_create_room_access_section_knocking_option_title" = "Poproś o dołączenie"; +"screen_join_room_cancel_knock_action" = "Anuluj prośbę"; +"screen_join_room_cancel_knock_alert_confirmation" = "Tak, anuluj"; +"screen_join_room_cancel_knock_alert_description" = "Czy na pewno chcesz anulować prośbę o dołączenie do tego pokoju?"; +"screen_join_room_cancel_knock_alert_title" = "Anuluj prośbę o dołączenie"; +"screen_join_room_knock_message_description" = "Wiadomość (opcjonalne)"; +"screen_join_room_knock_sent_description" = "Otrzymasz zaproszenie dołączenia do pokoju, jeśli prośba zostanie zaakceptowana."; +"screen_join_room_knock_sent_title" = "Wysłano prośbę o dołączenie"; "screen_pinned_timeline_empty_state_description" = "Naciśnij wiadomość i wybierz “%1$@”, aby dołączyć tutaj."; "screen_pinned_timeline_empty_state_headline" = "Przypinaj ważne wiadomości, aby można było je łatwo znaleźć"; "screen_reset_encryption_password_error" = "Wystąpił nieznany błąd. Sprawdź, czy hasło jest poprawne i spróbuj ponownie."; @@ -376,7 +380,7 @@ "screen_room_pinned_banner_loading_description" = "Wczytywanie wiadomości..."; "screen_room_pinned_banner_view_all_button_title" = "Wyświetl wszystkie"; "screen_room_details_pinned_events_row_title" = "Przypięte wiadomości"; -"screen_roomlist_knock_event_sent_description" = "Request to join sent"; +"screen_roomlist_knock_event_sent_description" = "Wysłano prośbę o dołączenie"; "screen_timeline_item_menu_send_failure_changed_identity" = "Wiadomość nie została wysłana, ponieważ tożsamość %1$@ uległa zmianie."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Wiadomość nie została wysłana, ponieważ %1$@ nie zweryfikował wszystkich urządzeń."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Wiadomość nie została wysłana, ponieważ nie zweryfikowałeś jednego lub więcej swoich urządzeń."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Zamierzasz założyć konto na %@"; "screen_advanced_settings_developer_mode" = "Tryb programisty"; "screen_advanced_settings_developer_mode_description" = "Włącz, aby uzyskać dostęp do funkcji dla deweloperów."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "Wyłącz edytor tekstu bogatego, aby pisać tekst Markdown ręcznie."; "screen_advanced_settings_send_read_receipts" = "Potwierdzenia odczytania"; "screen_advanced_settings_send_read_receipts_description" = "Gdy wyłączona, Twoje potwierdzenia odczytania nie zostaną wysłane. Potwierdzenia od innych wciąż będą odbierane."; @@ -456,14 +460,14 @@ "screen_change_server_title" = "Wybierz swój serwer"; "screen_chat_backup_key_backup_action_disable" = "Wyłącz backup"; "screen_chat_backup_key_backup_action_enable" = "Włącz backup"; -"screen_chat_backup_key_backup_description" = "Backup zapewnia, że nie stracisz swojej historii wiadomości. %1$@"; -"screen_chat_backup_key_backup_title" = "Backup"; -"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_key_backup_description" = "Bezpiecznie przechowuj swoją tożsamość kryptograficzną i klucze wiadomości na serwerze. Umożliwi to przeglądanie historii wiadomości na każdym nowym urządzeniu. %1$@"; +"screen_chat_backup_key_backup_title" = "Magazyn kluczy"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; +"screen_chat_backup_key_storage_toggle_description" = "Prześlij klucze z tego urządzenia"; +"screen_chat_backup_key_storage_toggle_title" = "Zezwól na magazynowanie kluczy"; "screen_chat_backup_recovery_action_change" = "Zmień klucz przywracania"; -"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_change_description" = "Odzyskaj swoją tożsamość kryptograficzną i historię wiadomości za pomocą klucza przywracania, jeśli utraciłeś dostęp do wszystkich swoich urządzeń."; "screen_chat_backup_recovery_action_confirm_description" = "Backup czatu jest niezsynchronizowany."; -"screen_chat_backup_recovery_action_setup" = "Skonfiguruj przywracanie"; "screen_chat_backup_recovery_action_setup_description" = "Uzyskaj dostęp do swoich wiadomości szyfrowanych, jeśli utracisz wszystkie swoje urządzenia lub zostaniesz wylogowany z %1$@."; "screen_create_account_title" = "Utwórz konto"; "screen_create_new_recovery_key_list_item_1" = "Otwórz %1$@ na urządzeniu stacjonarnym"; @@ -483,10 +487,10 @@ "screen_create_poll_title" = "Utwórz ankietę"; "screen_create_room_action_create_room" = "Nowy pokój"; "screen_create_room_error_creating_room" = "Wystąpił błąd w trakcie tworzenia pokoju"; -"screen_create_room_private_option_description" = "Wiadomości w tym pokoju są szyfrowane. Szyfrowania nie można później wyłączyć."; -"screen_create_room_private_option_title" = "Pokój prywatny (tylko zaproszenie)"; -"screen_create_room_public_option_description" = "Wiadomości nie są szyfrowane i każdy może je odczytać. Możesz aktywować szyfrowanie później."; -"screen_create_room_public_option_title" = "Pokój publiczny (wszyscy)"; +"screen_create_room_private_option_description" = "Tylko zaproszone osoby mogą dołączyć do tego pokoju. Wszystkie wiadomości są szyfrowane end-to-end."; +"screen_create_room_private_option_title" = "Pokój prywatny"; +"screen_create_room_public_option_description" = "Każdy może znaleźć ten pokój.\nMożesz to zmienić w ustawieniach pokoju."; +"screen_create_room_public_option_title" = "Pokój publiczny"; "screen_create_room_topic_label" = "Temat (opcjonalnie)"; "screen_deactivate_account_confirmation_dialog_content" = "Potwierdź dezaktywacje konta. Tej akcji nie można cofnąć."; "screen_deactivate_account_delete_all_messages" = "Usuń wszystkie moje wiadomości"; @@ -528,7 +532,7 @@ "screen_invites_empty_list" = "Brak zaproszeń"; "screen_invites_invited_you" = "%1$@ (%2$@) zaprosił Cię"; "screen_join_room_join_action" = "Dołącz do pokoju"; -"screen_join_room_knock_action" = "Zapukaj, by dołączyć"; +"screen_join_room_knock_action" = "Wyślij prośbę o dołączenie"; "screen_join_room_space_not_supported_description" = "%1$@ jeszcze nie obsługuje przestrzeni. Uzyskaj dostęp do przestrzeni w wersji web."; "screen_join_room_space_not_supported_title" = "Przestrzenie nie są jeszcze obsługiwane"; "screen_join_room_subtitle_knock" = "Kliknij przycisk poniżej, aby powiadomić administratora pokoju. Po zatwierdzeniu będziesz mógł dołączyć do rozmowy."; @@ -619,7 +623,7 @@ "screen_qr_code_login_initial_state_item_3" = "Wybierz %1$@"; "screen_qr_code_login_initial_state_item_3_action" = "“Powiąż nowe urządzenie”"; "screen_qr_code_login_initial_state_item_4" = "Zeskanuj kod QR za pomocą tego urządzenia"; -"screen_qr_code_login_initial_state_subtitle" = "Only available if your account provider supports it."; +"screen_qr_code_login_initial_state_subtitle" = "Dostępne tylko wtedy, gdy Twój dostawca konta obsługuje tę funkcję."; "screen_qr_code_login_initial_state_title" = "Otwórz %1$@ na innym urządzeniu, aby uzyskać kod QR"; "screen_qr_code_login_invalid_scan_state_description" = "Użyj kodu QR widocznego na drugim urządzeniu."; "screen_qr_code_login_invalid_scan_state_subtitle" = "Błędny kod QR"; @@ -647,14 +651,14 @@ "screen_recovery_key_copied_to_clipboard" = "Skopiowano klucz przywracania"; "screen_recovery_key_generating_key" = "Generuję..."; "screen_recovery_key_save_action" = "Zapisz klucz przywracania"; -"screen_recovery_key_save_description" = "Zapisz klucz przywracania w bezpiecznym miejscu lub zapisz go w menedżerze haseł."; +"screen_recovery_key_save_description" = "Zapisz klucz przywracania w bezpiecznym miejscu, np. w menedżerze haseł, notatce szyfrowanej lub sejfie."; "screen_recovery_key_save_key_description" = "Stuknij, by skopiować klucz przywracania"; "screen_recovery_key_save_title" = "Zapisz klucz przywracania"; "screen_recovery_key_setup_confirmation_description" = "Po tym kroku nie będziesz mieć dostępu do nowego klucza przywracania."; "screen_recovery_key_setup_confirmation_title" = "Czy zapisałeś swój klucz przywracania?"; "screen_recovery_key_setup_description" = "Backup czatu jest chroniony przez klucz przywracania. Jeśli potrzebujesz utworzyć nowy klucz, możesz to zrobić wybierając `Zmień klucz przywracania`."; "screen_recovery_key_setup_generate_key" = "Wygeneruj klucz przywracania"; -"screen_recovery_key_setup_generate_key_description" = "Upewnij się, że klucz przywracania możesz przechowywać w bezpiecznym miejscu"; +"screen_recovery_key_setup_generate_key_description" = "Nie udostępniaj tego nikomu!"; "screen_recovery_key_setup_success" = "Skonfigurowano przywracanie pomyślnie"; "screen_recovery_key_setup_title" = "Skonfiguruj przywracanie"; "screen_report_content_block_user_hint" = "Sprawdź, czy chcesz ukryć wszystkie bieżące i przyszłe wiadomości od tego użytkownika."; @@ -728,8 +732,8 @@ "screen_room_member_details_unblock_alert_action" = "Odblokuj"; "screen_room_member_details_unblock_alert_description" = "Będziesz mógł ponownie zobaczyć wszystkie wiadomości od tego użytkownika."; "screen_room_member_details_unblock_user" = "Odblokuj użytkownika"; -"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user."; -"screen_room_member_details_verify_button_title" = "Verify %1$@"; +"screen_room_member_details_verify_button_subtitle" = "Użyj aplikacji internetowej, aby zweryfikować tego użytkownika."; +"screen_room_member_details_verify_button_title" = "Zweryfikuj %1$@"; "screen_room_member_list_ban_member_confirmation_action" = "Zbanuj"; "screen_room_member_list_ban_member_confirmation_description" = "Nie będą mogli ponownie dołączyć do tego pokoju, jeśli zostaną zaproszeni."; "screen_room_member_list_ban_member_confirmation_title" = "Czy na pewno chcesz zbanować tego członka?"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Pokaż mniej"; "screen_room_timeline_message_copied" = "Skopiowano wiadomość"; "screen_room_timeline_no_permission_to_post" = "Nie masz uprawnień, aby pisać w tym pokoju"; -"screen_room_timeline_reactions_show_less" = "Pokaż mniej"; "screen_room_timeline_reactions_show_more" = "Pokaż więcej"; "screen_room_timeline_read_marker_title" = "Nowe"; "screen_room_title" = "Czat"; @@ -835,14 +838,13 @@ "screen_session_verification_positive_button_verifying_ongoing" = "Oczekiwanie na dopasowanie"; "screen_session_verification_ready_subtitle" = "Porównaj unikalny zestaw emoji."; "screen_session_verification_request_accepted_subtitle" = "Porównaj unikalne emoji, upewniając się, że pojawiły się w tej samej kolejności."; -"screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; -"screen_session_verification_request_failure_title" = "Verification failed"; -"screen_session_verification_request_footer" = "Only continue if you initiated this verification."; -"screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; +"screen_session_verification_request_details_timestamp" = "Zalogowano"; +"screen_session_verification_request_failure_title" = "Weryfikacja nie powiodła się"; +"screen_session_verification_request_footer" = "Kontynuuj tylko, jeśli to Ty zainicjowałeś tę weryfikację."; +"screen_session_verification_request_subtitle" = "Zweryfikuj drugie urządzenie, aby zabezpieczyć historię wiadomości."; "screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; -"screen_session_verification_request_success_title" = "Device verified"; -"screen_session_verification_request_title" = "Verification requested"; +"screen_session_verification_request_success_title" = "Urządzenie zweryfikowane"; +"screen_session_verification_request_title" = "Zażądano weryfikacji"; "screen_session_verification_they_dont_match" = "Nie pasują do siebie"; "screen_session_verification_they_match" = "Pasują do siebie"; "screen_session_verification_waiting_to_accept_subtitle" = "Zaakceptuj prośbę o rozpoczęcie procesu weryfikacji w innej sesji, aby kontynuować."; @@ -956,7 +958,7 @@ "troubleshoot_notifications_screen_notice" = "Uruchom testy, aby wykryć potencjalne problemy z konfiguracją, jeśli powiadomienia nie działają prawidłowo."; "troubleshoot_notifications_screen_quick_fix_action" = "Spróbuj naprawić"; "troubleshoot_notifications_screen_success" = "Wszystkie testy przebiegły pomyślnie."; -"troubleshoot_notifications_screen_title" = "Powiadomienia rozwiązywania problemów"; +"troubleshoot_notifications_screen_title" = "Rozwiązywanie problemów powiadomień"; "troubleshoot_notifications_screen_waiting" = "Niektóre testy wymagają Twojej uwagi. Sprawdź szczegóły."; "troubleshoot_notifications_test_check_permission_description" = "Sprawdź, czy aplikacja może wyświetlać powiadomienia."; "troubleshoot_notifications_test_check_permission_title" = "Sprawdź uprawnienia"; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "Nie znaleziono dystrybutorów push."; "troubleshoot_notifications_test_unified_push_title" = "Sprawdź UnifiedPush"; "a11y_poll" = "Ankieta"; +"banner_set_up_recovery_submit" = "Skonfiguruj przywracanie"; "dialog_title_error" = "Błąd"; "dialog_title_success" = "Sukces"; "notification_fallback_content" = "Powiadomienie"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Odblokuj użytkownika"; "screen_bug_report_rash_logs_alert_title" = "%1$@ uległ awarii podczas ostatniego użycia. Czy chcesz przesłać nam raport o awarii?"; "screen_chat_backup_recovery_action_confirm" = "Wprowadź klucz przywracania"; +"screen_chat_backup_recovery_action_setup" = "Skonfiguruj przywracanie"; "screen_create_poll_cancel_confirmation_content_ios" = "Zmiany nie zostaną zapisane"; "screen_create_room_add_people_title" = "Zaproś znajomych"; "screen_create_room_room_name_label" = "Nazwa pokoju"; @@ -1028,8 +1032,8 @@ "screen_login_subtitle" = "Matrix to otwarta sieć do bezpiecznej i zdecentralizowanej komunikacji."; "screen_notification_settings_mentions_section_title" = "Wzmianki"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Spróbuj ponownie"; -"screen_recovery_key_change_generate_key_description" = "Upewnij się, że klucz przywracania możesz przechowywać w bezpiecznym miejscu"; -"screen_recovery_key_confirm_title" = "Wprowadź swój klucz przywracania"; +"screen_recovery_key_change_generate_key_description" = "Nie udostępniaj tego nikomu!"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Zablokuj użytkownika"; "screen_reset_encryption_password_placeholder" = "Wprowadź..."; "screen_room_attachment_source_camera_photo" = "Zrób zdjęcie"; @@ -1052,11 +1056,13 @@ "screen_room_error_failed_processing_media" = "Przetwarzanie multimediów do przesłania nie powiodło się, spróbuj ponownie."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Usuń i zbanuj członka"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Tylko wzmianki i słowa kluczowe"; +"screen_room_timeline_reactions_show_less" = "Pokaż mniej"; "screen_roomlist_filter_people" = "Osoby"; "screen_server_confirmation_change_server" = "Zmień dostawcę konta"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "Wyloguj"; "screen_signout_confirmation_dialog_title" = "Wyloguj"; "screen_signout_key_backup_offline_title" = "Twoje klucze są nadal archiwizowane"; "screen_signout_preference_item" = "Wyloguj"; "screen_signout_save_recovery_key_title" = "Czy zapisałeś swój klucz przywracania?"; -"troubleshoot_notifications_entry_point_title" = "Powiadomienia rozwiązywania problemów"; +"troubleshoot_notifications_entry_point_title" = "Rozwiązywanie problemów powiadomień"; diff --git a/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings b/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings index d958aa6757..659c5bf139 100644 --- a/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pt-BR.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; "banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; -"banner.set_up_recovery.title" = "Set up recovery"; +"banner_set_up_recovery_content" = "Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices."; +"banner_set_up_recovery_title" = "Set up recovery to protect your account"; "common_about" = "Sobre"; "common_acceptable_use_policy" = "Política de uso aceitável"; "common_advanced_settings" = "Configurações avançadas"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Verification failed"; "common_verified" = "Verified"; "common_verify_device" = "Verificar dispositivo"; +"common_verify_identity" = "Verify identity"; "common_video" = "Vídeo"; "common_voice_message" = "Mensagem de voz"; "common_waiting" = "Esperando..."; @@ -245,7 +246,9 @@ "common.you" = "You"; "common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; "common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; -"confirm_recovery_key_banner_message" = "Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup."; +"confirm_recovery_key_banner_message" = "Confirm your recovery key to maintain access to your key storage and message history."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "Insira sua chave de recuperação"; "crash_detection_dialog_content" = "%1$@ fechou inesperadamente na última vez que foi usado. Gostaria de compartilhar um relatório de falhas conosco?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "Desidentar"; "rich_text_editor_url_placeholder" = "Link"; "rich_text_editor_a11y_add_attachment" = "Adicionar anexo"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "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" = "URL inválida, por favor verifique se o protocolo (http/https) e o endereço correto estão presentes."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Você está prestes a criar uma conta em %@"; "screen_advanced_settings_developer_mode" = "Modo de desenvolvedor"; "screen_advanced_settings_developer_mode_description" = "Habilite para ter acesso a recursos e funcionalidades para desenvolvedores."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "Desative o editor de rich text para digitar Markdown manualmente."; "screen_advanced_settings_send_read_receipts" = "Confirmações de leitura"; "screen_advanced_settings_send_read_receipts_description" = "Se desligado, suas confirmações de leitura não serão enviadas para ninguém. Você ainda receberá confirmações de leitura de outros usuários."; @@ -458,12 +462,12 @@ "screen_chat_backup_key_backup_action_enable" = "Ativar o backup"; "screen_chat_backup_key_backup_description" = "O backup garante que você não perca seu histórico de mensagens. %1$@."; "screen_chat_backup_key_backup_title" = "Backup"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; "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" = "Alterar chave de recuperação"; "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" = "Seu backup das conversas está atualmente fora de sincronia."; -"screen_chat_backup_recovery_action_setup" = "Configurar a recuperação"; "screen_chat_backup_recovery_action_setup_description" = "Tenha acesso às suas mensagens criptografadas se você perder todos os seus dispositivos ou for desconectado do %1$@ em qualquer lugar."; "screen_create_account_title" = "Create account"; "screen_create_new_recovery_key_list_item_1" = "Open %1$@ in a desktop device"; @@ -638,7 +642,7 @@ "screen_recovery_key_change_title" = "Alterar chave de recuperação?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Create new recovery key"; "screen_recovery_key_confirm_description" = "Certifique-se de que ninguém possa ver essa tela!"; -"screen_recovery_key_confirm_error_content" = "Please try again to confirm access to your chat backup."; +"screen_recovery_key_confirm_error_content" = "Please try again to confirm access to your key storage."; "screen_recovery_key_confirm_error_title" = "Chave de recuperação incorreta"; "screen_recovery_key_confirm_key_description" = "Se você tiver uma chave de segurança ou frase de segurança, isso também funcionará."; "screen_recovery_key_confirm_key_placeholder" = "Inserir..."; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Mostrar menos"; "screen_room_timeline_message_copied" = "Mensagem copiada"; "screen_room_timeline_no_permission_to_post" = "Você não tem permissão para postar nesta sala"; -"screen_room_timeline_reactions_show_less" = "Mostrar menos"; "screen_room_timeline_reactions_show_more" = "Mostrar mais"; "screen_room_timeline_read_marker_title" = "Novo"; "screen_room_title" = "Chat"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "Compare um conjunto único de emojis."; "screen_session_verification_request_accepted_subtitle" = "Compare os emojis únicos, garantindo que apareçam na mesma ordem."; "screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "No push distributors found."; "troubleshoot_notifications_test_unified_push_title" = "Check UnifiedPush"; "a11y_poll" = "Enquete"; +"banner_set_up_recovery_submit" = "Configurar a recuperação"; "dialog_title_error" = "Erro"; "dialog_title_success" = "Sucesso"; "notification_fallback_content" = "Notificação"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Desbloquear usuário"; "screen_bug_report_rash_logs_alert_title" = "%1$@ fechou inesperadamente na última vez que foi usado. Gostaria de compartilhar um relatório de falhas conosco?"; "screen_chat_backup_recovery_action_confirm" = "Insira a chave de recuperação"; +"screen_chat_backup_recovery_action_setup" = "Configurar a recuperação"; "screen_create_poll_cancel_confirmation_content_ios" = "Suas alterações não serão salvas"; "screen_create_room_add_people_title" = "Convidar pessoas"; "screen_create_room_room_name_label" = "Nome da sala"; @@ -1029,7 +1033,7 @@ "screen_notification_settings_mentions_section_title" = "Menções"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Tente novamente"; "screen_recovery_key_change_generate_key_description" = "Certifique-se de que você pode armazenar sua chave de recuperação em algum lugar seguro"; -"screen_recovery_key_confirm_title" = "Insira sua chave de recuperação"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Bloquear usuário"; "screen_reset_encryption_password_placeholder" = "Inserir..."; "screen_room_attachment_source_camera_photo" = "Tirar foto"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Falha ao processar mídia para upload. Tente novamente."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Remover e banir membro"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Somente menções e palavras-chave"; +"screen_room_timeline_reactions_show_less" = "Mostrar menos"; "screen_roomlist_filter_people" = "Pessoas"; "screen_server_confirmation_change_server" = "Alterar provedor da conta"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "Sair"; "screen_signout_confirmation_dialog_title" = "Sair"; "screen_signout_key_backup_offline_title" = "O backup das suas chaves ainda está em andamento"; diff --git a/ElementX/Resources/Localizations/pt.lproj/Localizable.strings b/ElementX/Resources/Localizations/pt.lproj/Localizable.strings index 885001c5db..b4a06c13f7 100644 --- a/ElementX/Resources/Localizations/pt.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/pt.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "O teu servidor suporta agora um protocolo novo e mais rápido. Termina a sessão e volta a iniciar sessão para atualizar agora. Se o fizeres agora, evitarás um fim de sessão forçado quando o protocolo antigo for removido mais tarde."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Seu homeserver não suporta mais o protocolo antigo. Termine sessão e volte a iniciar sessão para continuar a utilizar a aplicação."; "banner_migrate_to_native_sliding_sync_title" = "Atualização disponível"; -"banner.set_up_recovery.content" = "Gere uma nova chave de recuperação que pode ser usada para restaurar seu histórico de mensagens criptografadas caso você perca o acesso aos seus dispositivos."; -"banner.set_up_recovery.title" = "Configurar a recuperação"; +"banner_set_up_recovery_content" = "Gere uma nova chave de recuperação que pode ser usada para restaurar seu histórico de mensagens criptografadas caso você perca o acesso aos seus dispositivos."; +"banner_set_up_recovery_title" = "Configurar a recuperação"; "common_about" = "Sobre"; "common_acceptable_use_policy" = "Política de utilização aceitável"; "common_advanced_settings" = "Configurações avançadas"; @@ -139,7 +139,7 @@ "common_edited_suffix" = "(editada)"; "common_editing" = "A editar"; "common_emote" = "* %1$@ %2$@"; -"common_encryption" = "Encryption"; +"common_encryption" = "Encriptação"; "common_encryption_enabled" = "Cifragem ativada"; "common_enter_your_pin" = "Introduz o teu PIN"; "common_error" = "Erro"; @@ -150,7 +150,7 @@ "common_favourited" = "Favoritas"; "common_file" = "Ficheiro"; "common_forward_message" = "Reencaminhar mensagem"; -"common_frequently_used" = "Frequently used"; +"common_frequently_used" = "Frequentemente utilizado"; "common_gif" = "GIF"; "common_image" = "Imagem"; "common_in_reply_to" = "Em resposta a %1$@"; @@ -230,9 +230,10 @@ "common_username" = "Nome de utilizador"; "common_verification_cancelled" = "Verificação cancelada"; "common_verification_complete" = "Verificação concluída"; -"common_verification_failed" = "Verification failed"; +"common_verification_failed" = "A verificação falhou"; "common_verified" = "Verificado"; "common_verify_device" = "Verificar o dispositivo"; +"common_verify_identity" = "Verifica a identidade"; "common_video" = "Vídeo"; "common_voice_message" = "Mensagem de voz"; "common_waiting" = "A aguardar…"; @@ -243,10 +244,12 @@ "common.pinned" = "Afixado"; "common.send_to" = "Enviar para"; "common.you" = "Você"; -"common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; -"common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; -"confirm_recovery_key_banner_message" = "A tua cópia de segurança das conversas está atualmente dessincronizada. Tens de inserir a tua chave de recuperação para manteres o acesso à cópia."; -"confirm_recovery_key_banner_title" = "Insere a tua chave de recuperação"; +"common_unable_to_decrypt_insecure_device" = "Enviado de um dispositivo inseguro"; +"common_unable_to_decrypt_verification_violation" = "A identidade verificada do remetente foi alterada"; +"confirm_recovery_key_banner_message" = "Confirma a tua chave de recuperação para manteres o acesso ao teu armazenamento de chaves e ao histórico de mensagens."; +"confirm_recovery_key_banner_primary_button_title" = "Introduz a tua chave de recuperação"; +"confirm_recovery_key_banner_secondary_button_title" = "Esqueceste-te da tua chave de recuperação?"; +"confirm_recovery_key_banner_title" = "O teu armazenamento de chaves não está sincronizado"; "crash_detection_dialog_content" = "A %1$@ teve uma falha da última vez que foi utilizada. Gostarias de partilhar um relatório de acidente connosco?"; "crypto_identity_change_pin_violation" = "A identidade de %1$@ parece ter mudado. %2$@"; "crypto_identity_change_pin_violation_new" = "A identidade de %1$@ (username: %2$@ ) aparenta ter mudado. %3$@"; @@ -341,21 +344,22 @@ "rich_text_editor_unindent" = "Desindentar"; "rich_text_editor_url_placeholder" = "Ligação"; "rich_text_editor_a11y_add_attachment" = "Adicionar anexo"; +"rich_text_editor_composer_caption_placeholder" = "Legenda opcional..."; "screen_advanced_settings_element_call_base_url" = "URL base para Element Call personalizado"; "screen_advanced_settings_element_call_base_url_description" = "Define um URL base para a Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL inválido, certifica-te de que incluis o protocolo (http/https) e o endereço correto."; -"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_room_address_section_footer" = "Para que esta sala seja visível no diretório público de salas, precisas de um endereço de sala."; +"screen_create_room_room_address_section_title" = "Endereço da sala"; +"screen_create_room_room_visibility_section_title" = "Visibilidade da sala"; "screen_create_room_access_section_anyone_option_description" = "Qualquer pessoa pode entrar nesta sala"; "screen_create_room_access_section_anyone_option_title" = "Qualquer pessoa"; "screen_create_room_access_section_header" = "Acesso à sala"; "screen_create_room_access_section_knocking_option_description" = "Qualquer pessoa pode pedir para entrar na sala, mas um administrador ou um moderador terá de aceitar o pedido"; "screen_create_room_access_section_knocking_option_title" = "Pedir para participar"; "screen_join_room_cancel_knock_action" = "Cancelar pedido"; -"screen_join_room_cancel_knock_alert_confirmation" = "Yes, cancel"; -"screen_join_room_cancel_knock_alert_description" = "Are you sure that you want to cancel your request to join this room?"; -"screen_join_room_cancel_knock_alert_title" = "Cancel request to join"; +"screen_join_room_cancel_knock_alert_confirmation" = "Sim, cancelar"; +"screen_join_room_cancel_knock_alert_description" = "Tens a certeza de que queres cancelar o teu pedido de entrada nesta sala?"; +"screen_join_room_cancel_knock_alert_title" = "Cancela o pedido de adesão"; "screen_join_room_knock_message_description" = "Mensagem (opcional)"; "screen_join_room_knock_sent_description" = "Irá receber um convite para participar na sala se seu pedido for aceite."; "screen_join_room_knock_sent_title" = "Pedido de adesão enviado"; @@ -376,7 +380,7 @@ "screen_room_pinned_banner_loading_description" = "A carregar mensagem..."; "screen_room_pinned_banner_view_all_button_title" = "Ver todas"; "screen_room_details_pinned_events_row_title" = "Mensagens afixadas"; -"screen_roomlist_knock_event_sent_description" = "Request to join sent"; +"screen_roomlist_knock_event_sent_description" = "Pedido de adesão enviado"; "screen_timeline_item_menu_send_failure_changed_identity" = "Mensagem não enviada porque a identidade verificada de %1$@ foi alterada."; "screen_timeline_item_menu_send_failure_unsigned_device" = "Mensagem não enviada porque %1$@ não verificou todos os dispositivos."; "screen_timeline_item_menu_send_failure_you_unsigned_device" = "Mensagem não enviada porque não verificou um ou mais dos seus dispositivos."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Irás criar uma conta em %@"; "screen_advanced_settings_developer_mode" = "Modo de programador"; "screen_advanced_settings_developer_mode_description" = "Permite o acesso a funcionalidades para programadores."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Carrega fotos e vídeos mais rapidamente e reduz a utilização de dados"; +"screen_advanced_settings_media_compression_title" = "Otimiza a qualidade da mídia"; "screen_advanced_settings_rich_text_editor_description" = "Desativa o editor de texto rico para poderes escrever Markdown manualmente."; "screen_advanced_settings_send_read_receipts" = "Recibos de leitura"; "screen_advanced_settings_send_read_receipts_description" = "Se desativada, os teus recibos de leitura não serão enviados a ninguém. Continuas a receber recibos de leitura de outros utilizadores."; @@ -456,14 +460,14 @@ "screen_change_server_title" = "Seleciona o teu servidor"; "screen_chat_backup_key_backup_action_disable" = "Desativar a cópia de segurança"; "screen_chat_backup_key_backup_action_enable" = "Ativar a cópia de segurança"; -"screen_chat_backup_key_backup_description" = "A cópia de segurança garante que não perdes o teu histórico de mensagens. %1$@."; -"screen_chat_backup_key_backup_title" = "Cópia de segurança"; -"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_key_backup_description" = "Guarda a tua identidade criptográfica e as chaves de mensagens de forma segura no servidor. Isto permitir-te-á ver o teu histórico de mensagens em qualquer dispositivo novo. %1$@."; +"screen_chat_backup_key_backup_title" = "Armazenamento de chaves"; +"screen_chat_backup_key_storage_disabled_error" = "O armazenamento de chaves deve ser ativado para configurar a recuperação."; +"screen_chat_backup_key_storage_toggle_description" = "Carrega chaves a partir deste dispositivo"; +"screen_chat_backup_key_storage_toggle_title" = "Permite o armazenamento de chaves"; "screen_chat_backup_recovery_action_change" = "Alterar chave de recuperação"; -"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" = "A tua cópia de segurança das conversas está atualmente dessincronizada."; -"screen_chat_backup_recovery_action_setup" = "Configurar recuperação"; +"screen_chat_backup_recovery_action_change_description" = "Recupera a tua identidade criptográfica e o histórico de mensagens com uma chave de recuperação, caso tenhas perdido todos os teus dispositivos existentes."; +"screen_chat_backup_recovery_action_confirm_description" = "O teu armazenamento de chaves está atualmente dessincronizado."; "screen_chat_backup_recovery_action_setup_description" = "Obtém acesso às tuas mensagens cifradas mesmo se perderes todos os teus dispositivos ou se terminares todas as tuas sessões %1$@."; "screen_create_account_title" = "Criar conta"; "screen_create_new_recovery_key_list_item_1" = "Abre a %1$@ num computador"; @@ -483,10 +487,10 @@ "screen_create_poll_title" = "Criar sondagem"; "screen_create_room_action_create_room" = "Nova sala"; "screen_create_room_error_creating_room" = "Ocorreu um erro ao criar a sala"; -"screen_create_room_private_option_description" = "As mensagens serão cifradas. Uma vez ativada, não é possível desativar a cifragem."; -"screen_create_room_private_option_title" = "Sala privada (entrada apenas por convite)"; -"screen_create_room_public_option_description" = "As mensagens não serão cifradas e qualquer um as poderá ler. É possível ativar a cifragem posteriormente."; -"screen_create_room_public_option_title" = "Sala pública (entrada livre)"; +"screen_create_room_private_option_description" = "Apenas as pessoas convidadas podem aceder a esta sala. Todas as mensagens são encriptadas ponta a ponta."; +"screen_create_room_private_option_title" = "Sala privada"; +"screen_create_room_public_option_description" = "Qualquer um pode encontrar esta sala. \nPode alterar esta opção nas definições da sala."; +"screen_create_room_public_option_title" = "Sala pública"; "screen_create_room_topic_label" = "Descrição (opcional)"; "screen_deactivate_account_confirmation_dialog_content" = "Confirme que pretende desativar a sua conta. Esta ação não pode ser desfeita."; "screen_deactivate_account_delete_all_messages" = "Eliminar todas as minhas mensagens"; @@ -638,7 +642,7 @@ "screen_recovery_key_change_title" = "Alterar a chave de recuperação?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Criar nova chave de recuperação"; "screen_recovery_key_confirm_description" = "Certifica-te de que ninguém consegue ver esta página!"; -"screen_recovery_key_confirm_error_content" = "Por favor, tenta novamente para confirmar o acesso à tua cópia de segurança das conversas."; +"screen_recovery_key_confirm_error_content" = "Tenta novamente para confirmar o acesso ao teu armazenamento de chaves."; "screen_recovery_key_confirm_error_title" = "Chave de recuperação incorreta"; "screen_recovery_key_confirm_key_description" = "Também funciona se tiveres uma chave ou frase de segurança."; "screen_recovery_key_confirm_key_placeholder" = "Inserir..."; @@ -647,14 +651,14 @@ "screen_recovery_key_copied_to_clipboard" = "Chave de recuperação copiada"; "screen_recovery_key_generating_key" = "A gerar…"; "screen_recovery_key_save_action" = "Guardar chave"; -"screen_recovery_key_save_description" = "Anota a tua chave de recuperação num local seguro ou guarda-a num gestor de senhas."; +"screen_recovery_key_save_description" = "Anota esta chave de recuperação num local seguro, como um gestor de palavras-passe, uma nota encriptada ou um cofre físico."; "screen_recovery_key_save_key_description" = "Toca para copiar a chave de recuperação"; "screen_recovery_key_save_title" = "Guarda a tua chave de recuperação"; "screen_recovery_key_setup_confirmation_description" = "Não poderás aceder à tua nova chave de recuperação após este passo."; "screen_recovery_key_setup_confirmation_title" = "Guardaste a tua chave de recuperação?"; "screen_recovery_key_setup_description" = "A tua cópia de segurança das conversas está protegida por uma chave de recuperação. Se precisares de uma nova chave após a configuração, podes recriá-la selecionando \"Alterar chave de recuperação\"."; "screen_recovery_key_setup_generate_key" = "Gerar a tua chave de recuperação"; -"screen_recovery_key_setup_generate_key_description" = "Certifica-te de que podes guardar a tua chave de recuperação num local seguro"; +"screen_recovery_key_setup_generate_key_description" = "Não partilhes isto com ninguém!"; "screen_recovery_key_setup_success" = "Recuperação configurada com sucesso"; "screen_recovery_key_setup_title" = "Configurar recuperação"; "screen_report_content_block_user_hint" = "Ativar para esconder todas as atuais e futuras mensagens deste utilizador"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Mostrar menos"; "screen_room_timeline_message_copied" = "Mensagem copiada"; "screen_room_timeline_no_permission_to_post" = "Não tens permissão para publicar nesta sala"; -"screen_room_timeline_reactions_show_less" = "Mostrar menos"; "screen_room_timeline_reactions_show_more" = "Mostrar mais"; "screen_room_timeline_read_marker_title" = "Novas"; "screen_room_title" = "Conversa"; @@ -827,7 +830,7 @@ "screen_session_verification_compare_numbers_title" = "Comparar números"; "screen_session_verification_complete_subtitle" = "A tua nova sessão está agora verificada, pelo que tem acesso às tuas mensagens cifradas e os outros utilizadores vão vê-la como de confiança."; "screen_session_verification_enter_recovery_key" = "Insere a chave de recuperação"; -"screen_session_verification_failed_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; +"screen_session_verification_failed_subtitle" = "O pedido expirou, o pedido foi recusado ou houve um erro de verificação."; "screen_session_verification_open_existing_session_subtitle" = "Prova que és tu para acederes ao teu histórico de mensagens cifradas."; "screen_session_verification_open_existing_session_title" = "Abrir sessão existente"; "screen_session_verification_positive_button_canceled" = "Repetir verificação"; @@ -836,12 +839,11 @@ "screen_session_verification_ready_subtitle" = "Compara um conjunto único de emojis."; "screen_session_verification_request_accepted_subtitle" = "Compara os emojis únicos, certificando-te de que aparecem pela mesma ordem."; "screen_session_verification_request_details_timestamp" = "Sessão iniciada"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; -"screen_session_verification_request_failure_title" = "Verification failed"; +"screen_session_verification_request_failure_title" = "A verificação falhou"; "screen_session_verification_request_footer" = "Continue apenas se tiver iniciado esta verificação."; "screen_session_verification_request_subtitle" = "Verifique o outro dispositivo para manter o histórico de mensagens seguro."; -"screen_session_verification_request_success_subtitle" = "Now you can read or send messages securely on your other device."; -"screen_session_verification_request_success_title" = "Device verified"; +"screen_session_verification_request_success_subtitle" = "Agora podes ler ou enviar mensagens de forma segura no teu outro dispositivo."; +"screen_session_verification_request_success_title" = "Dispositivo verificado"; "screen_session_verification_request_title" = "Verificação solicitada"; "screen_session_verification_they_dont_match" = "Não correspondem"; "screen_session_verification_they_match" = "Correspondem"; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "Nenhum distribuidor encontrado."; "troubleshoot_notifications_test_unified_push_title" = "Verificar UnifiedPush"; "a11y_poll" = "Sondagem"; +"banner_set_up_recovery_submit" = "Configurar recuperação"; "dialog_title_error" = "Erro"; "dialog_title_success" = "Sucesso"; "notification_fallback_content" = "Notificação"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Desbloquear utilizador"; "screen_bug_report_rash_logs_alert_title" = "A %1$@ teve uma falha da última vez que foi utilizada. Gostarias de partilhar um relatório de acidente connosco?"; "screen_chat_backup_recovery_action_confirm" = "Insere a chave de recuperação"; +"screen_chat_backup_recovery_action_setup" = "Configurar recuperação"; "screen_create_poll_cancel_confirmation_content_ios" = "As tuas alterações não serão guardadas"; "screen_create_room_add_people_title" = "Convidar pessoas"; "screen_create_room_room_name_label" = "Nome da sala"; @@ -1028,8 +1032,8 @@ "screen_login_subtitle" = "A Matrix é uma rede aberta de comunicação descentralizada e segura."; "screen_notification_settings_mentions_section_title" = "Menções"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Tentar novamente"; -"screen_recovery_key_change_generate_key_description" = "Certifica-te de que podes guardar a tua chave de recuperação num local seguro"; -"screen_recovery_key_confirm_title" = "Insere a tua chave de recuperação"; +"screen_recovery_key_change_generate_key_description" = "Não partilhes isto com ninguém!"; +"screen_recovery_key_confirm_title" = "Introduz a tua chave de recuperação"; "screen_report_content_block_user" = "Bloquear utilizador"; "screen_reset_encryption_password_placeholder" = "Inserir..."; "screen_room_attachment_source_camera_photo" = "Tirar foto"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Falha ao processar multimédia para carregamento, por favor tente novamente."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Remover e banir participante"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Menções ou palavras-chave"; +"screen_room_timeline_reactions_show_less" = "Mostrar menos"; "screen_roomlist_filter_people" = "Pessoas"; "screen_server_confirmation_change_server" = "Alterar operador de conta"; +"screen_session_verification_request_failure_subtitle" = "O pedido expirou, o pedido foi recusado ou houve um erro de verificação."; "screen_signout_confirmation_dialog_submit" = "Terminar sessão"; "screen_signout_confirmation_dialog_title" = "Terminar sessão"; "screen_signout_key_backup_offline_title" = "As tuas chaves ainda estão a ser guardadas"; diff --git a/ElementX/Resources/Localizations/ro.lproj/Localizable.strings b/ElementX/Resources/Localizations/ro.lproj/Localizable.strings index ee2a97a8a8..fdaff848fe 100644 --- a/ElementX/Resources/Localizations/ro.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ro.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; "banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; -"banner.set_up_recovery.title" = "Set up recovery"; +"banner_set_up_recovery_content" = "Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices."; +"banner_set_up_recovery_title" = "Set up recovery to protect your account"; "common_about" = "Despre"; "common_acceptable_use_policy" = "Politică de utilizare rezonabilă"; "common_advanced_settings" = "Setări avansate"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Verification failed"; "common_verified" = "Verified"; "common_verify_device" = "Verificați dispozitivul"; +"common_verify_identity" = "Verify identity"; "common_video" = "Video"; "common_voice_message" = "Mesaj vocal"; "common_waiting" = "Se aşteaptă…"; @@ -246,6 +247,8 @@ "common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; "common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Backup-ul pentru chat nu este sincronizat în prezent. Trebuie să confirmați cheia de recuperare pentru a menține accesul la backup."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "Confirmați cheia de recuperare"; "crash_detection_dialog_content" = "%1$@ s-a blocat ultima dată când a fost folosit. Doriți să ne trimiteți un raport?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "Dez-identare"; "rich_text_editor_url_placeholder" = "Link"; "rich_text_editor_a11y_add_attachment" = "Adăugați un atașament"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "screen_advanced_settings_element_call_base_url" = "Adresa URL de bază Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Setați o adresă URL de bază personalizată pentru Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "URL invalid, vă rugăm să vă asigurați că includeți protocolul (http/https) și adresa corectă."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Sunteți pe cale să creați un cont pe %@"; "screen_advanced_settings_developer_mode" = "Modul dezvoltator"; "screen_advanced_settings_developer_mode_description" = "Activați pentru a avea acces la funcționalități pentru dezvoltatori."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "Dezactivați editorul avansat pentru a tasta manual Markdown."; "screen_advanced_settings_send_read_receipts" = "Chitanțe de citire"; "screen_advanced_settings_send_read_receipts_description" = "Dacă dezactivată, chitanțele dumneavoastră de citire nu vor fi trimise nimănui. Veți primi în continuare chitanțe de citire de la alți utilizatori."; @@ -458,12 +462,12 @@ "screen_chat_backup_key_backup_action_enable" = "Activați backupul"; "screen_chat_backup_key_backup_description" = "Backup vă asigură că nu pierdeți istoricul mesajelor. %1$@."; "screen_chat_backup_key_backup_title" = "Backup"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; "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" = "Schimbați cheia de recuperare"; "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" = "Backup-ul pentru chat nu este sincronizat în prezent."; -"screen_chat_backup_recovery_action_setup" = "Configurați recuperarea"; "screen_chat_backup_recovery_action_setup_description" = "Obțineți acces la mesajele dumneavoastră criptate dacă vă pierdeți toate dispozitivele sau sunteți deconectat de la %1$@ peste tot."; "screen_create_account_title" = "Create account"; "screen_create_new_recovery_key_list_item_1" = "Deschideți %1$@ pe un dispozitiv desktop"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Afișați mai puțin"; "screen_room_timeline_message_copied" = "Mesaj copiat"; "screen_room_timeline_no_permission_to_post" = "Nu aveți permisiunea de a posta în această cameră"; -"screen_room_timeline_reactions_show_less" = "Afișați mai puțin"; "screen_room_timeline_reactions_show_more" = "Afișați mai mult"; "screen_room_timeline_read_marker_title" = "Nou"; "screen_room_title" = "Chat"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "Comparați un set unic de emoji-uri."; "screen_session_verification_request_accepted_subtitle" = "Comparăți emoticoalene asigurându-vă că apar în aceeași ordine."; "screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "Nu au fost găsiți distribuitori push."; "troubleshoot_notifications_test_unified_push_title" = "Verificați UnifiedPush"; "a11y_poll" = "Sondaj"; +"banner_set_up_recovery_submit" = "Configurați recuperarea"; "dialog_title_error" = "Eroare"; "dialog_title_success" = "Succes"; "notification_fallback_content" = "Notificare"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Deblocați utilizatorul"; "screen_bug_report_rash_logs_alert_title" = "%1$@ s-a blocat ultima dată când a fost folosit. Doriți să ne trimiteți un raport?"; "screen_chat_backup_recovery_action_confirm" = "Introduceți cheia de recuperare"; +"screen_chat_backup_recovery_action_setup" = "Configurați recuperarea"; "screen_create_poll_cancel_confirmation_content_ios" = "Modificările dumneavoastră nu vor fi salvate"; "screen_create_room_add_people_title" = "Invitați prieteni"; "screen_create_room_room_name_label" = "Numele camerei"; @@ -1029,7 +1033,7 @@ "screen_notification_settings_mentions_section_title" = "Mențiuni"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Încercați din nou"; "screen_recovery_key_change_generate_key_description" = "Asigurați-vă că puteți stoca cheia de recuperare undeva în siguranță"; -"screen_recovery_key_confirm_title" = "Confirmați cheia de recuperare"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Blocați utilizatorul"; "screen_reset_encryption_password_placeholder" = "Introduceți..."; "screen_room_attachment_source_camera_photo" = "Faceți o fotografie"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Procesarea datelor media a eșuat, vă rugăm să încercați din nou."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Eliminați și interziceți membrul"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Numai mențiuni și cuvinte cheie"; +"screen_room_timeline_reactions_show_less" = "Afișați mai puțin"; "screen_roomlist_filter_people" = "Persoane"; "screen_server_confirmation_change_server" = "Schimbați furnizorul contului"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "Deconectați-vă"; "screen_signout_confirmation_dialog_title" = "Deconectați-vă"; "screen_signout_key_backup_offline_title" = "Cheile dumneavoastră sunt încă în curs de backup"; diff --git a/ElementX/Resources/Localizations/ru.lproj/Localizable.strings b/ElementX/Resources/Localizations/ru.lproj/Localizable.strings index 48586797e2..c4b1205b07 100644 --- a/ElementX/Resources/Localizations/ru.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/ru.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Теперь ваш сервер поддерживает новый, более быстрый протокол. Чтобы обновить его прямо сейчас, выйдите и войдите в свою учётную запись снова. Сделав это сейчас, вы сможете избежать принудительного выхода из системы при последующем удалении старого протокола."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Ваш домашний сервер больше не поддерживает старый протокол. Пожалуйста, выйдите и войдите в свою учётную запись снова, чтобы продолжить использование приложения."; "banner_migrate_to_native_sliding_sync_title" = "Доступно обновление"; -"banner.set_up_recovery.content" = "Создайте новый ключ восстановления, который можно использовать для восстановления зашифрованной истории сообщений в случае потери доступа к своим устройствам."; -"banner.set_up_recovery.title" = "Настроить восстановление"; +"banner_set_up_recovery_content" = "Создайте новый ключ восстановления, который можно использовать для восстановления зашифрованной истории сообщений в случае потери доступа к своим устройствам."; +"banner_set_up_recovery_title" = "Для защиты вашего аккаунта рекомендуется настроить восстановление"; "common_about" = "О приложении"; "common_acceptable_use_policy" = "Политика допустимого использования"; "common_advanced_settings" = "Дополнительные настройки"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Сбой проверки"; "common_verified" = "Проверено"; "common_verify_device" = "Подтверждение устройства"; +"common_verify_identity" = "Подтвердить личность"; "common_video" = "Видео"; "common_voice_message" = "Голосовое сообщение"; "common_waiting" = "Ожидание…"; @@ -245,8 +246,10 @@ "common.you" = "Вы"; "common_unable_to_decrypt_insecure_device" = "Отправлено с незащищенного устройства"; "common_unable_to_decrypt_verification_violation" = "Подтвержденная личность отправителя изменилась"; -"confirm_recovery_key_banner_message" = "В настоящее время резервная копия ваших чатов не синхронизирована. Вам потребуется ввести свой ключ восстановления, чтобы сохранить доступ к резервной копии чатов."; -"confirm_recovery_key_banner_title" = "Введите ключ восстановления"; +"confirm_recovery_key_banner_message" = "Подтвердите ключ восстановления, чтобы сохранить доступ к хранилищу ключей и истории сообщений."; +"confirm_recovery_key_banner_primary_button_title" = "Введите ключ восстановления"; +"confirm_recovery_key_banner_secondary_button_title" = "Забыли ключ восстановления?"; +"confirm_recovery_key_banner_title" = "Хранилище ключей не синхронизировано"; "crash_detection_dialog_content" = "При последнем использовании %1$@ произошел сбой. Хотите поделиться отчетом о сбое?"; "crypto_identity_change_pin_violation" = "Судя по всему, идентификатор %1$@ изменился. %2$@"; "crypto_identity_change_pin_violation_new" = "Пользователь %1$@ сменил имя пользователя на %2$@. %3$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "Без отступа"; "rich_text_editor_url_placeholder" = "Ссылка"; "rich_text_editor_a11y_add_attachment" = "Прикрепить файл"; +"rich_text_editor_composer_caption_placeholder" = "Необязательный заголовок..."; "screen_advanced_settings_element_call_base_url" = "Базовый URL сервера звонков Element"; "screen_advanced_settings_element_call_base_url_description" = "Задайте свой сервер Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Адрес указан неверно, удостоверьтесь, что вы указали протокол (http/https) и правильный адрес."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Вы собираетесь создать учетную запись на %@"; "screen_advanced_settings_developer_mode" = "Режим разработчика"; "screen_advanced_settings_developer_mode_description" = "Предоставьте разработчикам доступ к функциям и функциональным возможностям."; -"screen_advanced_settings_media_compression_description" = "Оптимизировать для загрузки"; -"screen_advanced_settings_media_compression_title" = "Медиа"; +"screen_advanced_settings_media_compression_description" = "Загружайте фотографии и видео быстрее и сокращайте потребление трафика"; +"screen_advanced_settings_media_compression_title" = "Оптимизировать качество мультимедиа"; "screen_advanced_settings_rich_text_editor_description" = "Отключить редактор форматированного текста и включить Markdown."; "screen_advanced_settings_send_read_receipts" = "Уведомления о прочтении"; "screen_advanced_settings_send_read_receipts_description" = "Если этот параметр выключен, ваш статус о прочтении не будет отображаться. Вы по-прежнему будете видеть статус о прочтении от других пользователей."; @@ -454,16 +458,16 @@ "screen_change_server_form_notice" = "Вы можете подключиться только к существующему серверу, поддерживающему sliding sync. Администратору домашнего сервера потребуется настроить его. %1$@"; "screen_change_server_subtitle" = "Какой адрес у вашего сервера?"; "screen_change_server_title" = "Выберите свой сервер"; -"screen_chat_backup_key_backup_action_disable" = "Отключить резервное копирование"; +"screen_chat_backup_key_backup_action_disable" = "Удалить хранилище ключей"; "screen_chat_backup_key_backup_action_enable" = "Включить резервное копирование"; "screen_chat_backup_key_backup_description" = "Сохраните вашу криптографическую идентификацию и ключи сообщений в безопасности на сервере. Это позволит вам просматривать историю сообщений на любых новых устройствах.%1$@ ."; "screen_chat_backup_key_backup_title" = "Хранилище ключей"; +"screen_chat_backup_key_storage_disabled_error" = "Для настройки восстановления необходимо включить хранилище ключей."; "screen_chat_backup_key_storage_toggle_description" = "Загрузить ключи с этого устройства"; "screen_chat_backup_key_storage_toggle_title" = "Разрешить хранение ключей"; "screen_chat_backup_recovery_action_change" = "Изменить ключ восстановления"; "screen_chat_backup_recovery_action_change_description" = "Если вы потеряли все существующие устройства, то сможете восстановить свою криптографическую идентификацию и историю сообщений с помощью ключа восстановления"; "screen_chat_backup_recovery_action_confirm_description" = "В настоящее время резервная копия ваших чатов не синхронизирована."; -"screen_chat_backup_recovery_action_setup" = "Настроить восстановление"; "screen_chat_backup_recovery_action_setup_description" = "Получите доступ к зашифрованным сообщениям, если вы потеряете все свои устройства или выйдете из системы %1$@ отовсюду."; "screen_create_account_title" = "Создать учетную запись"; "screen_create_new_recovery_key_list_item_1" = "Откройте %1$@ на компьютере"; @@ -538,10 +542,10 @@ "screen_key_backup_disable_confirmation_action_turn_off" = "Выключить"; "screen_key_backup_disable_confirmation_description" = "Вы потеряете зашифрованные сообщения, если выйдете из всех устройств."; "screen_key_backup_disable_confirmation_title" = "Вы действительно хотите отключить резервное копирование?"; -"screen_key_backup_disable_description" = "Отключение резервного копирования удалит текущую резервную копию ключа шифрования и отключит другие функции безопасности. В этом случае вы выполните следующие действия:"; +"screen_key_backup_disable_description" = "Удаление хранилища ключей приведёт к удалению вашей криптографической идентификации и ключей сообщений с сервера, а также отключению следующих функций безопасности:"; "screen_key_backup_disable_description_point_1" = "Нет зашифрованной истории сообщений на новых устройствах"; "screen_key_backup_disable_description_point_2" = "Вы потеряете доступ к зашифрованным сообщениям, если выйдете из %1$@ везде"; -"screen_key_backup_disable_title" = "Вы действительно хотите отключить резервное копирование?"; +"screen_key_backup_disable_title" = "Вы уверены, что хотите отключить хранение ключей и удалить их?"; "screen_login_error_deactivated_account" = "Данная учётная запись была отключена."; "screen_login_error_invalid_credentials" = "Неверное имя пользователя и/или пароль"; "screen_login_error_invalid_user_id" = "Это не корректный идентификатор пользователя. Ожидаемый формат: '@user:homeserver.org'"; @@ -652,7 +656,7 @@ "screen_recovery_key_save_title" = "Сохраните ключ восстановления"; "screen_recovery_key_setup_confirmation_description" = "После этого шага вы не сможете получить доступ к новому ключу восстановления."; "screen_recovery_key_setup_confirmation_title" = "Вы сохранили ключ восстановления?"; -"screen_recovery_key_setup_description" = "Резервная копия чата защищена ключом восстановления. Если после настройки вам понадобится новый ключ восстановления, вы можете создать его заново, выбрав «Изменить ключ восстановления»."; +"screen_recovery_key_setup_description" = "Ваше хранилище ключей защищено ключом восстановления. Если после настройки вам понадобится новый ключ восстановления, вы можете его пересоздать, выбрав «Изменить ключ восстановления»."; "screen_recovery_key_setup_generate_key" = "Создайте ключ восстановления"; "screen_recovery_key_setup_generate_key_description" = "Не сообщайте эту информацию никому!"; "screen_recovery_key_setup_success" = "Настройка восстановления выполнена успешно"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Показать меньше"; "screen_room_timeline_message_copied" = "Сообщение скопировано"; "screen_room_timeline_no_permission_to_post" = "У вас нет разрешения публиковать сообщения в этой комнате"; -"screen_room_timeline_reactions_show_less" = "Показать меньше"; "screen_room_timeline_reactions_show_more" = "Показать больше"; "screen_room_timeline_read_marker_title" = "Новый"; "screen_room_title" = "Чат"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "Сравните уникальный набор эмодзи."; "screen_session_verification_request_accepted_subtitle" = "Сравните уникальные смайлики, убедившись, что они расположены в том же порядке."; "screen_session_verification_request_details_timestamp" = "Вход выполнен"; -"screen_session_verification_request_failure_subtitle" = "Время ожидания подтверждения истекло, запрос был отклонён, или при подтверждении произошло несоответствие."; "screen_session_verification_request_failure_title" = "Сбой проверки"; "screen_session_verification_request_footer" = "Продолжайте только если вы ожидали данное подтверждение."; "screen_session_verification_request_subtitle" = "Чтобы сохранить историю сообщений в безопасности, проверьте другое устройство."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "Поставщиков push-уведомлений не найдено."; "troubleshoot_notifications_test_unified_push_title" = "Проверка UnifiedPush"; "a11y_poll" = "Опрос"; +"banner_set_up_recovery_submit" = "Настроить восстановление"; "dialog_title_error" = "Ошибка"; "dialog_title_success" = "Успешно"; "notification_fallback_content" = "Уведомление"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Разблокировать пользователя"; "screen_bug_report_rash_logs_alert_title" = "При последнем использовании %1$@ произошел сбой. Хотите поделиться отчетом о сбое?"; "screen_chat_backup_recovery_action_confirm" = "Введите ключ восстановления"; +"screen_chat_backup_recovery_action_setup" = "Настроить восстановление"; "screen_create_poll_cancel_confirmation_content_ios" = "Ваши изменения не будут сохранены"; "screen_create_room_add_people_title" = "Пригласить в комнату"; "screen_create_room_room_name_label" = "Название комнаты"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Не удалось обработать медиафайл для загрузки, попробуйте еще раз."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Удалить и заблокировать участника"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Только упоминания и ключевые слова"; +"screen_room_timeline_reactions_show_less" = "Показать меньше"; "screen_roomlist_filter_people" = "Пользователи"; "screen_server_confirmation_change_server" = "Сменить поставщика учетной записи"; +"screen_session_verification_request_failure_subtitle" = "Время ожидания подтверждения истекло, запрос был отклонён, или при подтверждении произошло несоответствие."; "screen_signout_confirmation_dialog_submit" = "Выйти"; "screen_signout_confirmation_dialog_title" = "Выйти"; "screen_signout_key_backup_offline_title" = "Резервное копирование ключей все еще продолжается"; diff --git a/ElementX/Resources/Localizations/sk.lproj/Localizable.strings b/ElementX/Resources/Localizations/sk.lproj/Localizable.strings index 9ea9cef956..1008f9615c 100644 --- a/ElementX/Resources/Localizations/sk.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/sk.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Váš server teraz podporuje nový, rýchlejší protokol. Odhláste sa a prihláste sa znova, aby ste mohli aktualizovať. Ak to urobíte teraz, pomôže vám vyhnúť sa nútenému odhláseniu, keď sa starý protokol neskôr odstráni."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Váš domovský server už nepodporuje starý protokol. Ak chcete pokračovať v používaní aplikácie, odhláste sa a znova sa prihláste."; "banner_migrate_to_native_sliding_sync_title" = "Aktualizácia je k dispozícii"; -"banner.set_up_recovery.content" = "Vytvorte nový kľúč na obnovenie, ktorý môžete použiť na obnovenie vašej histórie šifrovaných správ v prípade straty prístupu k vašim zariadeniam."; -"banner.set_up_recovery.title" = "Nastaviť obnovenie"; +"banner_set_up_recovery_content" = "Vytvorte nový kľúč na obnovenie, ktorý môžete použiť na obnovenie vašej histórie šifrovaných správ v prípade straty prístupu k vašim zariadeniam."; +"banner_set_up_recovery_title" = "Nastaviť obnovenie"; "common_about" = "O aplikácii"; "common_acceptable_use_policy" = "Zásady prijateľného používania"; "common_advanced_settings" = "Pokročilé nastavenia"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Overenie zlyhalo"; "common_verified" = "Overené"; "common_verify_device" = "Overiť zariadenie"; +"common_verify_identity" = "Verify identity"; "common_video" = "Video"; "common_voice_message" = "Hlasová správa"; "common_waiting" = "Čaká sa…"; @@ -246,6 +247,8 @@ "common_unable_to_decrypt_insecure_device" = "Odoslané z nezabezpečeného zariadenia"; "common_unable_to_decrypt_verification_violation" = "Overená totožnosť odosielateľa sa zmenila"; "confirm_recovery_key_banner_message" = "Vaša záloha konverzácie nie je momentálne synchronizovaná. Na zachovanie prístupu k zálohe konverzácie musíte potvrdiť svoj kľúč na obnovu."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "Potvrďte svoj kľúč na obnovenie"; "crash_detection_dialog_content" = "%1$@ zlyhal pri poslednom použití. Chcete zdieľať správu o páde s našim tímom?"; "crypto_identity_change_pin_violation" = "Zdá sa, že totožnosť používateľa %1$@ sa zmenila.%2$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "Zrušiť odsadenie"; "rich_text_editor_url_placeholder" = "Odkaz"; "rich_text_editor_a11y_add_attachment" = "Pridať prílohu"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "screen_advanced_settings_element_call_base_url" = "Vlastná Element Call základná URL adresa"; "screen_advanced_settings_element_call_base_url_description" = "Nastaviť vlastnú základnú URL adresu pre Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Neplatná adresa URL, uistite sa, že ste uviedli protokol (http/https) a správnu adresu."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Chystáte sa vytvoriť účet na %@"; "screen_advanced_settings_developer_mode" = "Vývojársky režim"; "screen_advanced_settings_developer_mode_description" = "Umožniť prístup k možnostiam a funkciám pre vývojárov."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "Vypnite rozšírený textový editor na ručné písanie Markdown."; "screen_advanced_settings_send_read_receipts" = "Potvrdenia o prečítaní"; "screen_advanced_settings_send_read_receipts_description" = "Ak je táto funkcia vypnutá, vaše potvrdenia o prečítaní sa nebudú nikomu odosielať. Stále budete dostávať potvrdenia o prečítaní od ostatných používateľov."; @@ -458,12 +462,12 @@ "screen_chat_backup_key_backup_action_enable" = "Zapnúť zálohovanie"; "screen_chat_backup_key_backup_description" = "Uložte svoju kryptografickú identitu a kľúče správ bezpečne na server. To vám umožní zobraziť históriu správ na všetkých nových zariadeniach. %1$@."; "screen_chat_backup_key_backup_title" = "Úložisko kľúčov"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; "screen_chat_backup_key_storage_toggle_description" = "Nahrať kľúče z tohto zariadenia"; "screen_chat_backup_key_storage_toggle_title" = "Povoliť úložisko kľúčov"; "screen_chat_backup_recovery_action_change" = "Zmeniť kľúč na obnovenie"; "screen_chat_backup_recovery_action_change_description" = "Obnovte svoju kryptografickú totožnosť a históriu správ pomocou kľúča na obnovenie, ak ste stratili všetky svoje existujúce zariadenia."; "screen_chat_backup_recovery_action_confirm_description" = "Vaša záloha konverzácie nie je momentálne synchronizovaná."; -"screen_chat_backup_recovery_action_setup" = "Nastaviť obnovovanie"; "screen_chat_backup_recovery_action_setup_description" = "Získajte prístup k vašim šifrovaným správam aj keď stratíte všetky svoje zariadenia alebo sa odhlásite zo všetkých %1$@ zariadení."; "screen_create_account_title" = "Vytvoriť účet"; "screen_create_new_recovery_key_list_item_1" = "Otvoriť %1$@ v stolnom počítači"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Zobraziť menej"; "screen_room_timeline_message_copied" = "Správa skopírovaná"; "screen_room_timeline_no_permission_to_post" = "Nemáte povolenie uverejňovať príspevky v tejto miestnosti"; -"screen_room_timeline_reactions_show_less" = "Zobraziť menej"; "screen_room_timeline_reactions_show_more" = "Zobraziť viac"; "screen_room_timeline_read_marker_title" = "Nové"; "screen_room_title" = "Konverzácia"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "Porovnajte jedinečnú sadu emotikonov."; "screen_session_verification_request_accepted_subtitle" = "Porovnajte jedinečné emotikony a uistite sa, že sú zobrazené v rovnakom poradí."; "screen_session_verification_request_details_timestamp" = "Prihlásený"; -"screen_session_verification_request_failure_subtitle" = "Buď žiadosť vypršala, žiadosť bola zamietnutá, alebo došlo k nesúladu overovania."; "screen_session_verification_request_failure_title" = "Overenie zlyhalo"; "screen_session_verification_request_footer" = "Pokračujte iba vtedy, ak ste toto overenie začali."; "screen_session_verification_request_subtitle" = "Overte druhé zariadenie, aby bola vaša história správ zabezpečená."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "Nenašli sa žiadni distribútori push."; "troubleshoot_notifications_test_unified_push_title" = "Skontrolovať UnifiedPush"; "a11y_poll" = "Anketa"; +"banner_set_up_recovery_submit" = "Nastaviť obnovenie"; "dialog_title_error" = "Chyba"; "dialog_title_success" = "Úspech"; "notification_fallback_content" = "Oznámenie"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Odblokovať používateľa"; "screen_bug_report_rash_logs_alert_title" = "%1$@ zlyhal pri poslednom použití. Chcete zdieľať správu o páde s našim tímom?"; "screen_chat_backup_recovery_action_confirm" = "Zadajte kľúč na obnovenie"; +"screen_chat_backup_recovery_action_setup" = "Nastaviť obnovenie"; "screen_create_poll_cancel_confirmation_content_ios" = "Vaše zmeny nebudú uložené"; "screen_create_room_add_people_title" = "Pozvať ľudí"; "screen_create_room_room_name_label" = "Názov miestnosti"; @@ -1029,7 +1033,7 @@ "screen_notification_settings_mentions_section_title" = "Zmienky"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Skúste to znova"; "screen_recovery_key_change_generate_key_description" = "Nezdieľajte to s nikým!"; -"screen_recovery_key_confirm_title" = "Potvrďte svoj kľúč na obnovenie"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Zablokovať používateľa"; "screen_reset_encryption_password_placeholder" = "Zadať..."; "screen_room_attachment_source_camera_photo" = "Urobiť fotku"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Odstrániť a zakázať člena"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Iba zmienky a kľúčové slová"; +"screen_room_timeline_reactions_show_less" = "Zobraziť menej"; "screen_roomlist_filter_people" = "Ľudia"; "screen_server_confirmation_change_server" = "Zmeniť poskytovateľa účtu"; +"screen_session_verification_request_failure_subtitle" = "Buď žiadosť vypršala, žiadosť bola zamietnutá, alebo došlo k nesúladu overovania."; "screen_signout_confirmation_dialog_submit" = "Odhlásiť sa"; "screen_signout_confirmation_dialog_title" = "Odhlásiť sa"; "screen_signout_key_backup_offline_title" = "Vaše kľúče sa ešte stále zálohujú"; diff --git a/ElementX/Resources/Localizations/sv.lproj/Localizable.strings b/ElementX/Resources/Localizations/sv.lproj/Localizable.strings index 4154418ba1..579f9bff91 100644 --- a/ElementX/Resources/Localizations/sv.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/sv.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Din server stöder nu ett nytt, snabbare protokoll. Logga ut och logga in igen för att uppgradera nu. Om du gör detta nu hjälper du dig att undvika en tvingad utloggning när det gamla protokollet tas bort senare."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; "banner_migrate_to_native_sliding_sync_title" = "Uppgradering tillgänglig"; -"banner.set_up_recovery.content" = "Skapa en ny återställningsnyckel som kan användas för att återställa din krypterade meddelandehistorik om du förlorar åtkomst till dina enheter."; -"banner.set_up_recovery.title" = "Ställ in återställning"; +"banner_set_up_recovery_content" = "Skapa en ny återställningsnyckel som kan användas för att återställa din krypterade meddelandehistorik om du förlorar åtkomst till dina enheter."; +"banner_set_up_recovery_title" = "Ställ in återställning"; "common_about" = "Om"; "common_acceptable_use_policy" = "Policy för godtagbar användning"; "common_advanced_settings" = "Avancerade inställningar"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Verification failed"; "common_verified" = "Verified"; "common_verify_device" = "Verifiera enheten"; +"common_verify_identity" = "Verify identity"; "common_video" = "Video"; "common_voice_message" = "Röstmeddelande"; "common_waiting" = "Väntar …"; @@ -246,6 +247,8 @@ "common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; "common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Din chattsäkerhetskopia är för närvarande inte synkroniserad. Du måste ange din återställningsnyckel för att behålla åtkomsten till din chattsäkerhetskopia."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "Ange din återställningsnyckel"; "crash_detection_dialog_content" = "%1$@ kraschade senast den användes. Vill du dela en kraschrapport med oss?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "Ta bort indrag"; "rich_text_editor_url_placeholder" = "Länk"; "rich_text_editor_a11y_add_attachment" = "Lägg till bilaga"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "screen_advanced_settings_element_call_base_url" = "Anpassad bas-URL för Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Ange en anpassad bas-URL för Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Ogiltig URL, se till att du inkluderar protokollet (http/https) och rätt adress."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Du är på väg att skapa ett konto på %@"; "screen_advanced_settings_developer_mode" = "Utvecklarläge"; "screen_advanced_settings_developer_mode_description" = "Aktivera för att ha tillgång till funktionalitet för utvecklare."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "Inaktivera rik-text-redigeraren för att skriva Markdown manuellt."; "screen_advanced_settings_send_read_receipts" = "Läskvitton"; "screen_advanced_settings_send_read_receipts_description" = "Om det är avstängt kommer dina läskvitton inte att skickas till någon. Du kommer fortfarande att få läskvitton från andra användare."; @@ -458,12 +462,12 @@ "screen_chat_backup_key_backup_action_enable" = "Slå på säkerhetskopiering"; "screen_chat_backup_key_backup_description" = "Säkerhetskopior ser till att du inte blir av med din meddelandehistorik. %1$@."; "screen_chat_backup_key_backup_title" = "Nyckellagring"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; "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" = "Byt återställningsnyckel"; "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" = "Din chattsäkerhetskopia är för närvarande osynkroniserad."; -"screen_chat_backup_recovery_action_setup" = "Ställ in återställning"; "screen_chat_backup_recovery_action_setup_description" = "Få tillgång till dina krypterade meddelanden om du tappar bort alla dina enheter eller blir utloggad ur %1$@ överallt."; "screen_create_account_title" = "Create account"; "screen_create_new_recovery_key_list_item_1" = "Öppna %1$@ på en skrivbordsenhet"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Visa mindre"; "screen_room_timeline_message_copied" = "Meddelande kopierat"; "screen_room_timeline_no_permission_to_post" = "Du är inte behörig att göra inlägg i det här rummet"; -"screen_room_timeline_reactions_show_less" = "Visa mindre"; "screen_room_timeline_reactions_show_more" = "Visa mer"; "screen_room_timeline_read_marker_title" = "Nytt"; "screen_room_title" = "Chatt"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "Jämför en unik uppsättning emojis."; "screen_session_verification_request_accepted_subtitle" = "Jämför de unika emojierna och se till att de visas i samma ordning."; "screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "Inga push-distributörer hittades."; "troubleshoot_notifications_test_unified_push_title" = "Kontrollera UnifiedPush"; "a11y_poll" = "Omröstning"; +"banner_set_up_recovery_submit" = "Ställ in återställning"; "dialog_title_error" = "Fel"; "dialog_title_success" = "Lyckades"; "notification_fallback_content" = "notis"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Avblockera användare"; "screen_bug_report_rash_logs_alert_title" = "%1$@ kraschade senast den användes. Vill du dela en kraschrapport med oss?"; "screen_chat_backup_recovery_action_confirm" = "Ange återställningsnyckel"; +"screen_chat_backup_recovery_action_setup" = "Ställ in återställning"; "screen_create_poll_cancel_confirmation_content_ios" = "Dina ändringar kommer inte att sparas"; "screen_create_room_add_people_title" = "Bjud in personer"; "screen_create_room_room_name_label" = "Rumsnamn"; @@ -1029,7 +1033,7 @@ "screen_notification_settings_mentions_section_title" = "Omnämnanden"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Försök igen"; "screen_recovery_key_change_generate_key_description" = "Se till att du kan lagra din återställningsnyckel någonstans säkert"; -"screen_recovery_key_confirm_title" = "Ange din återställningsnyckel"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Blockera användare"; "screen_reset_encryption_password_placeholder" = "Ange …"; "screen_room_attachment_source_camera_photo" = "Ta ett foto"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Misslyckades att bearbeta media för uppladdning, vänligen pröva igen."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Ta bort och banna medlem"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Endast omnämnanden och nyckelord"; +"screen_room_timeline_reactions_show_less" = "Visa mindre"; "screen_roomlist_filter_people" = "Personer"; "screen_server_confirmation_change_server" = "Byt kontoleverantör"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "Logga ut"; "screen_signout_confirmation_dialog_title" = "Logga ut"; "screen_signout_key_backup_offline_title" = "Dina nycklar säkerhetskopieras fortfarande"; diff --git a/ElementX/Resources/Localizations/uk.lproj/Localizable.strings b/ElementX/Resources/Localizations/uk.lproj/Localizable.strings index 4fa6bb8ef6..6e5b02cfdd 100644 --- a/ElementX/Resources/Localizations/uk.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/uk.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; "banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; -"banner.set_up_recovery.title" = "Set up recovery"; +"banner_set_up_recovery_content" = "Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices."; +"banner_set_up_recovery_title" = "Set up recovery to protect your account"; "common_about" = "Відомості"; "common_acceptable_use_policy" = "Політика прийнятного використання"; "common_advanced_settings" = "Додаткові налаштування"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Verification failed"; "common_verified" = "Verified"; "common_verify_device" = "Перевірте пристрій"; +"common_verify_identity" = "Verify identity"; "common_video" = "Відео"; "common_voice_message" = "Голосове повідомлення"; "common_waiting" = "Очікування..."; @@ -246,6 +247,8 @@ "common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; "common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "Ваша резервна копія чату наразі не синхронізована. Вам потрібно підтвердити ключ відновлення, щоб зберегти доступ до резервної копії чату."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "Підтвердіть ключ відновлення"; "crash_detection_dialog_content" = "%1$@ аварійно завершив роботу під час останнього використання. Бажаєте поділитися з нами звітом про збій?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "Без відступу"; "rich_text_editor_url_placeholder" = "Посилання"; "rich_text_editor_a11y_add_attachment" = "Додати вкладення"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "screen_advanced_settings_element_call_base_url" = "Користувацька URL-адреса Element Call"; "screen_advanced_settings_element_call_base_url_description" = "Встановіть URL-адресу для Element Call."; "screen_advanced_settings_element_call_base_url_validation_error" = "Неправильна URL-адреса, будь ласка, переконайтеся, що ви вказали протокол (http/https) та правильну адресу."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Ви збираєтеся створити обліковий запис на %@"; "screen_advanced_settings_developer_mode" = "Режим розробника"; "screen_advanced_settings_developer_mode_description" = "Увімкніть доступ до функцій і можливостей для розробників."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "Вимкніть редактор розширеного тексту, щоб вводити Markdown вручну."; "screen_advanced_settings_send_read_receipts" = "Читати журнали"; "screen_advanced_settings_send_read_receipts_description" = "Якщо вимкнено, ваші сповіщення про прочитання нікому не надсилатимуться. Ви все одно отримуватимете сповіщення про прочитання від інших користувачів."; @@ -458,12 +462,12 @@ "screen_chat_backup_key_backup_action_enable" = "Увімкнути резервне копіювання"; "screen_chat_backup_key_backup_description" = "Резервне копіювання гарантує, що ви не втратите історію повідомлень. %1$@."; "screen_chat_backup_key_backup_title" = "Резервне копіювання"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; "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" = "Змінити ключ відновлення"; "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" = "Ваша резервна копія чату наразі не синхронізована."; -"screen_chat_backup_recovery_action_setup" = "Налаштувати відновлення"; "screen_chat_backup_recovery_action_setup_description" = "Отримайте доступ до своїх зашифрованих повідомлень, якщо ви втратите всі свої пристрої або вийшли з %1$@ системи."; "screen_create_account_title" = "Create account"; "screen_create_new_recovery_key_list_item_1" = "Відкрийте %1$@ на комп'ютері"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Показувати менше"; "screen_room_timeline_message_copied" = "Повідомлення скопійовано"; "screen_room_timeline_no_permission_to_post" = "У Вас немає дозволу на публікацію в цій кімнаті"; -"screen_room_timeline_reactions_show_less" = "Показувати менше"; "screen_room_timeline_reactions_show_more" = "Показати більше"; "screen_room_timeline_read_marker_title" = "Нове"; "screen_room_title" = "Чат"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "Порівняйте унікальний набір емоджи."; "screen_session_verification_request_accepted_subtitle" = "Порівняйте унікальні емодзі, переконавшись, що вони відображаються в однаковому порядку."; "screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "Дистриб'юторів не знайдено."; "troubleshoot_notifications_test_unified_push_title" = "Перевірка UnifiedPush"; "a11y_poll" = "Опитування"; +"banner_set_up_recovery_submit" = "Налаштувати відновлення"; "dialog_title_error" = "Помилка"; "dialog_title_success" = "Успіх"; "notification_fallback_content" = "Сповіщення"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Розблокувати користувача"; "screen_bug_report_rash_logs_alert_title" = "%1$@ аварійно завершив роботу під час останнього використання. Бажаєте поділитися з нами звітом про збій?"; "screen_chat_backup_recovery_action_confirm" = "Введіть ключ відновлення"; +"screen_chat_backup_recovery_action_setup" = "Налаштувати відновлення"; "screen_create_poll_cancel_confirmation_content_ios" = "Внесені зміни не буде збережено"; "screen_create_room_add_people_title" = "Запросити людей"; "screen_create_room_room_name_label" = "Назва кімнати"; @@ -1029,7 +1033,7 @@ "screen_notification_settings_mentions_section_title" = "Згадки"; "screen_qr_code_login_invalid_scan_state_retry_button" = "Спробуйте ще раз"; "screen_recovery_key_change_generate_key_description" = "Переконайтеся, що ви можете зберігати ключ відновлення в безпечному місці"; -"screen_recovery_key_confirm_title" = "Підтвердіть ключ відновлення"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "Заблокувати користувача"; "screen_reset_encryption_password_placeholder" = "Ввести..."; "screen_room_attachment_source_camera_photo" = "Зробити фото"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Не вдалося обробити медіафайл для завантаження, спробуйте ще раз."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Вилучити й заблокувати учасника"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Тільки згадки та ключові слова"; +"screen_room_timeline_reactions_show_less" = "Показувати менше"; "screen_roomlist_filter_people" = "Люди"; "screen_server_confirmation_change_server" = "Змінити провайдера облікового запису"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "Вийти"; "screen_signout_confirmation_dialog_title" = "Вийти"; "screen_signout_key_backup_offline_title" = "Резервне копіювання ваших ключів ще триває"; diff --git a/ElementX/Resources/Localizations/uz.lproj/Localizable.strings b/ElementX/Resources/Localizations/uz.lproj/Localizable.strings index 07780a5617..a6242a13c4 100644 --- a/ElementX/Resources/Localizations/uz.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/uz.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; "banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; -"banner.set_up_recovery.title" = "Set up recovery"; +"banner_set_up_recovery_content" = "Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices."; +"banner_set_up_recovery_title" = "Set up recovery to protect your account"; "common_about" = "Haqida"; "common_acceptable_use_policy" = "Qabul qilinadigan foydalanish siyosati"; "common_advanced_settings" = "Kengaytirilgan sozlamalar"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Verification failed"; "common_verified" = "Verified"; "common_verify_device" = "Verify device"; +"common_verify_identity" = "Verify identity"; "common_video" = "Video"; "common_voice_message" = "Ovozli xabar"; "common_waiting" = "Kutilmoqda…"; @@ -245,8 +246,10 @@ "common.you" = "You"; "common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; "common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; -"confirm_recovery_key_banner_message" = "Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup."; -"confirm_recovery_key_banner_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_message" = "Confirm your recovery key to maintain access to your key storage and message history."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; +"confirm_recovery_key_banner_title" = "Your key storage is out of sync"; "crash_detection_dialog_content" = "%1$@oxirgi marta ishlatilganda qulab tushdi. Biz bilan nosozlik hisobotini baham ko'rmoqchimisiz?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; "crypto_identity_change_pin_violation_new" = "%1$@’s %2$@ identity appears to have changed. %3$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "Paragrafni bekor qilish"; "rich_text_editor_url_placeholder" = "Havola"; "rich_text_editor_a11y_add_attachment" = "Biriktirma qo'shing"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "screen_advanced_settings_element_call_base_url" = "Maxsus element qo‘ng‘iroqlar bazasi URL manzili"; "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."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "Siz %@da hisob yaratmoqchisiz"; "screen_advanced_settings_developer_mode" = "Dasturchi rejimi"; "screen_advanced_settings_developer_mode_description" = "Ishlab chiquvchilar uchun xususiyatlar va funksiyalarga kirishni yoqing."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "Boy matn muharriri o'chiring Markdown bilan qo'lda yozish uchun"; "screen_advanced_settings_send_read_receipts" = "Read receipts"; "screen_advanced_settings_send_read_receipts_description" = "If turned off, your read receipts won't be sent to anyone. You will still receive read receipts from other users."; @@ -458,12 +462,12 @@ "screen_chat_backup_key_backup_action_enable" = "Zaxiralashni yoqing"; "screen_chat_backup_key_backup_description" = "Zaxiralash xabarlar tarixini yo'qotmaslikni ta'minlaydi.%1$@."; "screen_chat_backup_key_backup_title" = "Zaxira"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; "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" = "Qayta tiklash kalitini o'zgartiring"; "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" = "Sizning chat zaxirangiz hozirda sinxronlashtirilmagan."; -"screen_chat_backup_recovery_action_setup" = "Qayta tiklashni sozlang"; "screen_chat_backup_recovery_action_setup_description" = "Agar barcha qurilmalaringizni yo‘qotib qo‘ysangiz yoki tizimdan chiqqan bo‘lsangiz, shifrlangan xabarlaringizga ruxsat oling%1$@ hamma joyda."; "screen_create_account_title" = "Create account"; "screen_create_new_recovery_key_list_item_1" = "Open %1$@ in a desktop device"; @@ -638,7 +642,7 @@ "screen_recovery_key_change_title" = "Qayta tiklash kaliti almashtirilsinmi?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Create new recovery key"; "screen_recovery_key_confirm_description" = "Hech kim bu ekranni kora olmasligiga ishonch hosil qiling!"; -"screen_recovery_key_confirm_error_content" = "Please try again to confirm access to your chat backup."; +"screen_recovery_key_confirm_error_content" = "Please try again to confirm access to your key storage."; "screen_recovery_key_confirm_error_title" = "Incorrect recovery key"; "screen_recovery_key_confirm_key_description" = "Agar sizda xavfsizlik kaliti yoki xavfsizlik iborasi bolsa, bu ham ishlaydi."; "screen_recovery_key_confirm_key_placeholder" = "Kirish…"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "Kamroq ko'rsatish"; "screen_room_timeline_message_copied" = "Xabar nusxalandi"; "screen_room_timeline_no_permission_to_post" = "Sizda bu xonaga post yozishga ruxsat yo‘q"; -"screen_room_timeline_reactions_show_less" = "Kamroq ko'rsatish"; "screen_room_timeline_reactions_show_more" = "Ko'proq ko'rsatish"; "screen_room_timeline_read_marker_title" = "Yangi"; "screen_room_title" = "Chat"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "Compare a unique set of emojis."; "screen_session_verification_request_accepted_subtitle" = "Noyob emojilarni solishtiring, ular bir xil tartibda paydo bo'lishiga ishonch hosil qiling."; "screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "No push distributors found."; "troubleshoot_notifications_test_unified_push_title" = "Check UnifiedPush"; "a11y_poll" = "So'ro'vnoma"; +"banner_set_up_recovery_submit" = "Qayta tiklashni sozlang"; "dialog_title_error" = "Xato"; "dialog_title_success" = "Muvaffaqiyat"; "notification_fallback_content" = "Bildirishnoma"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "Foydalanuvchini blokdan chiqarish"; "screen_bug_report_rash_logs_alert_title" = "%1$@oxirgi marta ishlatilganda qulab tushdi. Biz bilan nosozlik hisobotini baham ko'rmoqchimisiz?"; "screen_chat_backup_recovery_action_confirm" = "Enter recovery key"; +"screen_chat_backup_recovery_action_setup" = "Qayta tiklashni sozlang"; "screen_create_poll_cancel_confirmation_content_ios" = "Your changes won’t be saved"; "screen_create_room_add_people_title" = "Odamlarni taklif qiling"; "screen_create_room_room_name_label" = "Xona nomi"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Mediani yuklab bo‘lmadi, qayta urinib ko‘ring."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "Remove and ban member"; "screen_room_notification_settings_mode_mentions_and_keywords" = "Faqat eslatmalar va kalit so'zlar"; +"screen_room_timeline_reactions_show_less" = "Kamroq ko'rsatish"; "screen_roomlist_filter_people" = "Odamlar"; "screen_server_confirmation_change_server" = "Hisob provayderini o'zgartiring"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "Tizimdan chiqish"; "screen_signout_confirmation_dialog_title" = "Tizimdan chiqish"; "screen_signout_key_backup_offline_title" = "Your keys are still being backed up"; diff --git a/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings b/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings index 1d0e92c33c..82305720b9 100644 --- a/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/zh-Hans.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "您的服务器现在支持更快的新协议。现在登出并重新登录以进行升级。现在这样做可以帮助您避免在以后删除旧协议时被强制登出。"; "banner_migrate_to_native_sliding_sync_force_logout_title" = "您的服务器不再支持旧协议。请登出并重新登录以继续使用此应用。"; "banner_migrate_to_native_sliding_sync_title" = "有可用升级"; -"banner.set_up_recovery.content" = "生成新的恢复密钥,该密钥可用于在您无法访问设备时恢复加密的消息历史记录。"; -"banner.set_up_recovery.title" = "设置恢复"; +"banner_set_up_recovery_content" = "生成新的恢复密钥,该密钥可用于在您无法访问设备时恢复加密的消息历史记录。"; +"banner_set_up_recovery_title" = "设置恢复"; "common_about" = "关于"; "common_acceptable_use_policy" = "可接受的使用政策"; "common_advanced_settings" = "高级设置"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Verification failed"; "common_verified" = "Verified"; "common_verify_device" = "验证设备"; +"common_verify_identity" = "Verify identity"; "common_video" = "视频"; "common_voice_message" = "语音消息"; "common_waiting" = "等待..."; @@ -246,6 +247,8 @@ "common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; "common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; "confirm_recovery_key_banner_message" = "聊天备份目前不同步,需要输入恢复密钥才能访问聊天备份。"; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "输入恢复密钥"; "crash_detection_dialog_content" = "%1$@ 上次使用时崩溃了。想和我们分享崩溃报告吗?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "取消缩进"; "rich_text_editor_url_placeholder" = "链接"; "rich_text_editor_a11y_add_attachment" = "添加附件"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "screen_advanced_settings_element_call_base_url" = "自定义 Element Call URL"; "screen_advanced_settings_element_call_base_url_description" = "为 Element 通话设置根 URL。"; "screen_advanced_settings_element_call_base_url_validation_error" = "URL 无效,请确保包含协议(http/https)和正确的地址。"; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "您即将在 %@ 上创建一个帐户"; "screen_advanced_settings_developer_mode" = "开发者模式"; "screen_advanced_settings_developer_mode_description" = "允许开发人员访问特性和功能。"; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "禁用富文本编辑器,手动输入 Markdown。"; "screen_advanced_settings_send_read_receipts" = "已读回执"; "screen_advanced_settings_send_read_receipts_description" = "如果关闭,您的已读回执将不会发送给别人。您仍能收到别人的已读回执。"; @@ -458,12 +462,12 @@ "screen_chat_backup_key_backup_action_enable" = "开启备份"; "screen_chat_backup_key_backup_description" = "备份可确保你不会丢失消息历史记录。%1$@。"; "screen_chat_backup_key_backup_title" = "备份"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; "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" = "更改恢复密钥"; "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" = "您的聊天备份当前不同步。"; -"screen_chat_backup_recovery_action_setup" = "设置恢复密钥"; "screen_chat_backup_recovery_action_setup_description" = "在丢失或从 %1$@ 登出所有设备的情况下访问加密消息。"; "screen_create_account_title" = "Create account"; "screen_create_new_recovery_key_list_item_1" = "在桌面设备中打开 %1$@"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "折叠"; "screen_room_timeline_message_copied" = "消息已复制"; "screen_room_timeline_no_permission_to_post" = "您无权在此房间发言"; -"screen_room_timeline_reactions_show_less" = "折叠"; "screen_room_timeline_reactions_show_more" = "展开"; "screen_room_timeline_read_marker_title" = "新消息"; "screen_room_title" = "聊天"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "比较一组表情符号。"; "screen_session_verification_request_accepted_subtitle" = "比较表情符号,确保它们以相同顺序排列。"; "screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "未找到推送 distributor。"; "troubleshoot_notifications_test_unified_push_title" = "检查 UnifiedPush"; "a11y_poll" = "投票"; +"banner_set_up_recovery_submit" = "设置恢复"; "dialog_title_error" = "错误"; "dialog_title_success" = "成功"; "notification_fallback_content" = "通知"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "解封用户"; "screen_bug_report_rash_logs_alert_title" = "%1$@ 上次使用时崩溃了。想和我们分享崩溃报告吗?"; "screen_chat_backup_recovery_action_confirm" = "输入恢复密钥"; +"screen_chat_backup_recovery_action_setup" = "设置恢复"; "screen_create_poll_cancel_confirmation_content_ios" = "更改不会保存"; "screen_create_room_add_people_title" = "邀请朋友"; "screen_create_room_room_name_label" = "房间名称"; @@ -1029,7 +1033,7 @@ "screen_notification_settings_mentions_section_title" = "提及"; "screen_qr_code_login_invalid_scan_state_retry_button" = "再试一次"; "screen_recovery_key_change_generate_key_description" = "确保将恢复密钥存储在安全的地方"; -"screen_recovery_key_confirm_title" = "输入恢复密钥"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "封禁用户"; "screen_reset_encryption_password_placeholder" = "输入……"; "screen_room_attachment_source_camera_photo" = "拍摄照片"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "处理要上传的媒体失败,请重试。"; "screen_room_member_list_manage_member_remove_confirmation_ban" = "移除并封禁成员"; "screen_room_notification_settings_mode_mentions_and_keywords" = "仅限提及和关键词"; +"screen_room_timeline_reactions_show_less" = "折叠"; "screen_roomlist_filter_people" = "用户"; "screen_server_confirmation_change_server" = "更改账户提供者"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "登出"; "screen_signout_confirmation_dialog_title" = "登出"; "screen_signout_key_backup_offline_title" = "您的密钥仍在备份中"; diff --git a/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings b/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings index 458c278852..ebb04ae153 100644 --- a/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/zh-Hant-TW.lproj/Localizable.strings @@ -115,8 +115,8 @@ "banner_migrate_to_native_sliding_sync_description" = "Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."; "banner_migrate_to_native_sliding_sync_force_logout_title" = "Your homeserver no longer supports the old protocol. Please log out and log back in to continue using the app."; "banner_migrate_to_native_sliding_sync_title" = "Upgrade available"; -"banner.set_up_recovery.content" = "Generate a new recovery key that can be used to restore your encrypted message history in case you lose access to your devices."; -"banner.set_up_recovery.title" = "Set up recovery"; +"banner_set_up_recovery_content" = "Recover your cryptographic identity and message history with a recovery key if you have lost all your existing devices."; +"banner_set_up_recovery_title" = "Set up recovery to protect your account"; "common_about" = "關於"; "common_acceptable_use_policy" = "可接受使用政策"; "common_advanced_settings" = "進階設定"; @@ -233,6 +233,7 @@ "common_verification_failed" = "Verification failed"; "common_verified" = "Verified"; "common_verify_device" = "驗證裝置"; +"common_verify_identity" = "Verify identity"; "common_video" = "影片"; "common_voice_message" = "語音訊息"; "common_waiting" = "等待中..."; @@ -245,7 +246,9 @@ "common.you" = "You"; "common_unable_to_decrypt_insecure_device" = "Sent from an insecure device"; "common_unable_to_decrypt_verification_violation" = "Sender's verified identity has changed"; -"confirm_recovery_key_banner_message" = "Your chat backup is currently out of sync. You need to enter your recovery key to maintain access to your chat backup."; +"confirm_recovery_key_banner_message" = "Confirm your recovery key to maintain access to your key storage and message history."; +"confirm_recovery_key_banner_primary_button_title" = "Enter your recovery key"; +"confirm_recovery_key_banner_secondary_button_title" = "Forgot your recovery key?"; "confirm_recovery_key_banner_title" = "輸入您的復原金鑰"; "crash_detection_dialog_content" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; "crypto_identity_change_pin_violation" = "%1$@'s identity appears to have changed. %2$@"; @@ -341,6 +344,7 @@ "rich_text_editor_unindent" = "減少縮排"; "rich_text_editor_url_placeholder" = "連結"; "rich_text_editor_a11y_add_attachment" = "新增附件"; +"rich_text_editor_composer_caption_placeholder" = "Optional caption…"; "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."; @@ -388,8 +392,8 @@ "screen_account_provider_signup_title" = "您即將在 %@ 建立帳號"; "screen_advanced_settings_developer_mode" = "開發者模式"; "screen_advanced_settings_developer_mode_description" = "Enable to have access to features and functionality for developers."; -"screen_advanced_settings_media_compression_description" = "Optimize for upload"; -"screen_advanced_settings_media_compression_title" = "Media"; +"screen_advanced_settings_media_compression_description" = "Upload photos and videos faster and reduce data usage"; +"screen_advanced_settings_media_compression_title" = "Optimise media quality"; "screen_advanced_settings_rich_text_editor_description" = "手動輸入 Markdown,停用格式化文字編輯器。"; "screen_advanced_settings_send_read_receipts" = "已讀回條"; "screen_advanced_settings_send_read_receipts_description" = "If turned off, your read receipts won't be sent to anyone. You will still receive read receipts from other users."; @@ -458,12 +462,12 @@ "screen_chat_backup_key_backup_action_enable" = "開啟備份功能"; "screen_chat_backup_key_backup_description" = "備份可確保您不會遺失歷史訊息。%1$@。"; "screen_chat_backup_key_backup_title" = "備份"; +"screen_chat_backup_key_storage_disabled_error" = "Key storage must be turned on to set up recovery."; "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" = "變更復原金鑰"; "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_confirm_description" = "Your key storage is currently out of sync."; "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."; "screen_create_account_title" = "Create account"; "screen_create_new_recovery_key_list_item_1" = "Open %1$@ in a desktop device"; @@ -538,10 +542,10 @@ "screen_key_backup_disable_confirmation_action_turn_off" = "關閉"; "screen_key_backup_disable_confirmation_description" = "You will lose your encrypted messages if you are signed out of all devices."; "screen_key_backup_disable_confirmation_title" = "Are you sure you want to turn off backup?"; -"screen_key_backup_disable_description" = "Turning off backup will remove your current encryption key backup and turn off other security features. In this case, you will:"; -"screen_key_backup_disable_description_point_1" = "Not have encrypted message history on new devices"; -"screen_key_backup_disable_description_point_2" = "Lose access to your encrypted messages if you are signed out of %1$@ everywhere"; -"screen_key_backup_disable_title" = "Are you sure you want to turn off backup?"; +"screen_key_backup_disable_description" = "Deleting key storage will remove your cryptographic identity and message keys from the server and turn off the following security features:"; +"screen_key_backup_disable_description_point_1" = "You will not have encrypted message history on new devices"; +"screen_key_backup_disable_description_point_2" = "You will lose access to your encrypted messages if you are signed out of %1$@ everywhere"; +"screen_key_backup_disable_title" = "Are you sure you want to turn off key storage and delete it?"; "screen_login_error_deactivated_account" = "這個帳號已被停用。"; "screen_login_error_invalid_credentials" = "不正確的使用者名稱或密碼"; "screen_login_error_invalid_user_id" = "This is not a valid user identifier. Expected format: ‘@user:homeserver.org’"; @@ -638,7 +642,7 @@ "screen_recovery_key_change_title" = "Change recovery key?"; "screen_recovery_key_confirm_create_new_recovery_key" = "Create new recovery key"; "screen_recovery_key_confirm_description" = "Make sure nobody can see this screen!"; -"screen_recovery_key_confirm_error_content" = "Please try again to confirm access to your chat backup."; +"screen_recovery_key_confirm_error_content" = "Please try again to confirm access to your key storage."; "screen_recovery_key_confirm_error_title" = "Incorrect recovery key"; "screen_recovery_key_confirm_key_description" = "If you have a security key or security phrase, this will work too."; "screen_recovery_key_confirm_key_placeholder" = "Enter…"; @@ -652,7 +656,7 @@ "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_description" = "Your key storage is protected by a recovery key. If you need a new recovery key after setup, you can recreate it by selecting ‘Change recovery key’."; "screen_recovery_key_setup_generate_key" = "Generate your recovery key"; "screen_recovery_key_setup_generate_key_description" = "Do not share this with anyone!"; "screen_recovery_key_setup_success" = "Recovery setup successful"; @@ -786,7 +790,6 @@ "screen_room_timeline_less_reactions" = "較少"; "screen_room_timeline_message_copied" = "訊息已複製"; "screen_room_timeline_no_permission_to_post" = "您沒有權限在此聊天室傳送訊息"; -"screen_room_timeline_reactions_show_less" = "較少"; "screen_room_timeline_reactions_show_more" = "更多"; "screen_room_timeline_read_marker_title" = "新訊息"; "screen_room_title" = "Chat"; @@ -836,7 +839,6 @@ "screen_session_verification_ready_subtitle" = "比對一組唯一的表情符號。"; "screen_session_verification_request_accepted_subtitle" = "表情符號是唯一的,請相互比對,確認它們的排列順序是否相同。"; "screen_session_verification_request_details_timestamp" = "Signed in"; -"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_session_verification_request_failure_title" = "Verification failed"; "screen_session_verification_request_footer" = "Only continue if you initiated this verification."; "screen_session_verification_request_subtitle" = "Verify the other device to keep your message history secure."; @@ -992,6 +994,7 @@ "troubleshoot_notifications_test_unified_push_failure" = "No push distributors found."; "troubleshoot_notifications_test_unified_push_title" = "Check UnifiedPush"; "a11y_poll" = "投票"; +"banner_set_up_recovery_submit" = "Set up recovery"; "dialog_title_error" = "錯誤"; "dialog_title_success" = "成功"; "notification_fallback_content" = "通知"; @@ -1012,6 +1015,7 @@ "screen_blocked_users_unblock_alert_title" = "解除封鎖使用者"; "screen_bug_report_rash_logs_alert_title" = "%1$@ crashed the last time it was used. Would you like to share a crash report with us?"; "screen_chat_backup_recovery_action_confirm" = "Enter recovery key"; +"screen_chat_backup_recovery_action_setup" = "Set up recovery"; "screen_create_poll_cancel_confirmation_content_ios" = "您的變更不會儲存"; "screen_create_room_add_people_title" = "邀請夥伴"; "screen_create_room_room_name_label" = "聊天室名稱"; @@ -1029,7 +1033,7 @@ "screen_notification_settings_mentions_section_title" = "提及"; "screen_qr_code_login_invalid_scan_state_retry_button" = "再試一次"; "screen_recovery_key_change_generate_key_description" = "Do not share this with anyone!"; -"screen_recovery_key_confirm_title" = "輸入您的復原金鑰"; +"screen_recovery_key_confirm_title" = "Enter your recovery key"; "screen_report_content_block_user" = "封鎖使用者"; "screen_reset_encryption_password_placeholder" = "Enter…"; "screen_room_attachment_source_camera_photo" = "拍照"; @@ -1052,8 +1056,10 @@ "screen_room_error_failed_processing_media" = "Failed processing media to upload, please try again."; "screen_room_member_list_manage_member_remove_confirmation_ban" = "踢出並加入黑名單"; "screen_room_notification_settings_mode_mentions_and_keywords" = "僅限提及與關鍵字"; +"screen_room_timeline_reactions_show_less" = "較少"; "screen_roomlist_filter_people" = "夥伴"; "screen_server_confirmation_change_server" = "更改帳號提供者"; +"screen_session_verification_request_failure_subtitle" = "Either the request timed out, the request was denied, or there was a verification mismatch."; "screen_signout_confirmation_dialog_submit" = "登出"; "screen_signout_confirmation_dialog_title" = "登出"; "screen_signout_key_backup_offline_title" = "Your keys are still being backed up"; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 6f2f69e0bb..2c89b62e8c 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -780,6 +780,8 @@ internal enum L10n { internal static var richTextEditorCloseFormattingOptions: String { return L10n.tr("Localizable", "rich_text_editor_close_formatting_options") } /// Toggle code block internal static var richTextEditorCodeBlock: String { return L10n.tr("Localizable", "rich_text_editor_code_block") } + /// Optional caption… + internal static var richTextEditorComposerCaptionPlaceholder: String { return L10n.tr("Localizable", "rich_text_editor_composer_caption_placeholder") } /// Message… internal static var richTextEditorComposerPlaceholder: String { return L10n.tr("Localizable", "rich_text_editor_composer_placeholder") } /// Create a link From b9d05fe7f3867ba8d353dd4f8786254d2d0128ef Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 6 Nov 2024 12:41:02 +0200 Subject: [PATCH 107/114] Prevent the background task's expiration from stopping the main sync loop. --- .../Sources/Application/AppCoordinator.swift | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index eec3b77f94..012f248f1e 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -26,8 +26,6 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg /// Common background task to continue long-running tasks in the background. private var backgroundTask: UIBackgroundTaskIdentifier? - - private var isSuspended = false private var userSession: UserSessionProtocol? { didSet { @@ -929,8 +927,6 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg } } - isSuspended = true - // This does seem to work if scheduled from the background task above // Schedule it here instead but with an earliest being date of 30 seconds scheduleBackgroundAppRefresh() @@ -944,12 +940,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg appMediator.endBackgroundTask(backgroundTask) self.backgroundTask = nil } - - if isSuspended { - startSync() - } - - isSuspended = false + + startSync() } // MARK: Background app refresh @@ -989,7 +981,11 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg scheduleBackgroundAppRefresh() task.expirationHandler = { [weak self] in - self?.stopSync() // Attempt to stop the sync loop cleanly. + if UIApplication.shared.applicationState != .active { + // Attempt to stop the sync loop cleanly, only if the app not already running + self?.stopSync() + } + MXLog.info("Background app refresh task expired") task.setTaskCompleted(success: true) } From 10966ab620929517394ae1a4ceb9a458ff876c18 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 6 Nov 2024 13:00:11 +0200 Subject: [PATCH 108/114] Begin a background task when receiving a call in order to get state updates for longer. --- ElementX/Sources/Application/AppCoordinator.swift | 11 +++++++---- .../Services/ElementCall/ElementCallService.swift | 4 +++- .../ElementCall/ElementCallServiceProtocol.swift | 1 + 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index 012f248f1e..71ee3f0739 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -148,6 +148,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg switch action { case .startCall(let roomID): self?.handleAppRoute(.call(roomID: roomID)) + case .receivedIncomingCallRequest: + self?.scheduleDelayedSyncStop() default: break } @@ -912,6 +914,11 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg private func applicationWillResignActive() { MXLog.info("Application will resign active") + scheduleDelayedSyncStop() + scheduleBackgroundAppRefresh() + } + + private func scheduleDelayedSyncStop() { guard backgroundTask == nil else { return } @@ -926,10 +933,6 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg self.backgroundTask = nil } } - - // This does seem to work if scheduled from the background task above - // Schedule it here instead but with an earliest being date of 30 seconds - scheduleBackgroundAppRefresh() } @objc diff --git a/ElementX/Sources/Services/ElementCall/ElementCallService.swift b/ElementX/Sources/Services/ElementCall/ElementCallService.swift index 95e804d974..1ecf1dfa20 100644 --- a/ElementX/Sources/Services/ElementCall/ElementCallService.swift +++ b/ElementX/Sources/Services/ElementCall/ElementCallService.swift @@ -170,11 +170,13 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe // https://stackoverflow.com/a/41230020/730924 update.remoteHandle = .init(type: .generic, value: roomID) - callProvider.reportNewIncomingCall(with: callID.callKitID, update: update) { error in + callProvider.reportNewIncomingCall(with: callID.callKitID, update: update) { [weak self] error in if let error { MXLog.error("Failed reporting new incoming call with error: \(error)") } + self?.actionsSubject.send(.receivedIncomingCallRequest) + completion() } diff --git a/ElementX/Sources/Services/ElementCall/ElementCallServiceProtocol.swift b/ElementX/Sources/Services/ElementCall/ElementCallServiceProtocol.swift index 1247541e55..63bdd0c78e 100644 --- a/ElementX/Sources/Services/ElementCall/ElementCallServiceProtocol.swift +++ b/ElementX/Sources/Services/ElementCall/ElementCallServiceProtocol.swift @@ -8,6 +8,7 @@ import Combine enum ElementCallServiceAction { + case receivedIncomingCallRequest case startCall(roomID: String) case endCall(roomID: String) case setAudioEnabled(_ enabled: Bool, roomID: String) From 171fc2cdc5bdca2539299313cd9640fdf50788ba Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Wed, 6 Nov 2024 17:41:03 +0100 Subject: [PATCH 109/114] update sdk --- ElementX.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- ElementX/Sources/Services/Client/ClientProxy.swift | 4 +++- ElementX/Sources/Services/Client/ClientProxyProtocol.swift | 1 + project.yml | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 0c81278f56..3184afc21c 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7858,7 +7858,7 @@ repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.0.63; + version = 1.0.64; }; }; 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index eff2b29b05..b336c80344 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/matrix-rust-components-swift", "state" : { - "revision" : "0dcb3a3ee8b0b1bb92c2e9d6395202e2547fe0d8", - "version" : "1.0.63" + "revision" : "5b2a50a75fe5814e5be4c550717d0e8a1abdbc7b", + "version" : "1.0.64" } }, { diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 58acb6c317..801ce4e8cb 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -625,7 +625,9 @@ class ClientProxy: ClientProxyProtocol { func resolveRoomAlias(_ alias: String) async -> Result { do { - let resolvedAlias = try await client.resolveRoomAlias(roomAlias: alias) + guard let resolvedAlias = try await client.resolveRoomAlias(roomAlias: alias) else { + return .failure(.failedResolvingRoomAlias) + } // Resolving aliases is done through the directory/room API which returns too many / all known // vias, which in turn results in invalid join requests. Trim them to something manageable diff --git a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift index 5a28fb6a63..85bd4f7adf 100644 --- a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift +++ b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift @@ -36,6 +36,7 @@ enum ClientProxyError: Error { case failedUploadingMedia(Error, MatrixErrorCode) case roomPreviewIsPrivate case failedRetrievingUserIdentity + case failedResolvingRoomAlias } enum SlidingSyncConstants { diff --git a/project.yml b/project.yml index 1126076d92..b7ee5742e4 100644 --- a/project.yml +++ b/project.yml @@ -60,7 +60,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/element-hq/matrix-rust-components-swift - exactVersion: 1.0.63 + exactVersion: 1.0.64 # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios From 4ac527a9dc48ac1b59a3c0526aaaa50885cf0471 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 6 Nov 2024 20:08:59 +0200 Subject: [PATCH 110/114] Start syncing when receiving a background VoIP call for the cases in which the app was suspended but not terminated. --- ElementX/Sources/Application/AppCoordinator.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index 71ee3f0739..1a97ac8718 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -149,6 +149,10 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg case .startCall(let roomID): self?.handleAppRoute(.call(roomID: roomID)) case .receivedIncomingCallRequest: + // When reporting a VoIP call through the CXProvider's `reportNewIncomingVoIPPushPayload` + // the UIApplication states don't change and syncing is neither started nor ran on + // a background task. Handle both manually here. + self?.startSync() self?.scheduleDelayedSyncStop() default: break From 5ead649489894f2cbaae3948a16cb353abce67df Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Thu, 7 Nov 2024 08:41:46 +0200 Subject: [PATCH 111/114] Fastlane fails resetting the right simulator, use `device` instead of `destination`. --- fastlane/Fastfile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 881b9cf950..9e336b42d7 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -84,7 +84,7 @@ lane :unit_tests do |options| run_tests( scheme: "UnitTests", - destination: "platform=iOS Simulator,name=iPhone 16,OS=18.0", + device: "iPhone 16 (18.0)", ensure_devices_found: true, result_bundle: true, number_of_retries: 3, @@ -94,7 +94,7 @@ lane :unit_tests do |options| if !options[:skip_previews] run_tests( scheme: "PreviewTests", - destination: "platform=iOS Simulator,name=iPhone SE (3rd generation),OS=18.0", + device: "iPhone SE (3rd generation) (18.0)", ensure_devices_found: true, result_bundle: true, number_of_retries: 3, @@ -107,13 +107,13 @@ end lane :ui_tests do |options| create_simulator_if_necessary( - name: "iPhone 16", + name: "iPhone 16 (18.0)", type: "com.apple.CoreSimulator.SimDeviceType.iPhone-16", runtime: "com.apple.CoreSimulator.SimRuntime.iOS-18-0" ) create_simulator_if_necessary( - name: "iPad (10th generation)", + name: "iPad (10th generation) (18.0)", type: "com.apple.CoreSimulator.SimDeviceType.iPad-10th-generation", runtime: "com.apple.CoreSimulator.SimRuntime.iOS-18-0" ) @@ -128,7 +128,7 @@ lane :ui_tests do |options| run_tests( scheme: "UITests", - devices: ["iPhone 16", "iPad (10th generation)"], + devices: ["iPhone 16 (18.0)", "iPad (10th generation) (18.0)"], ensure_devices_found: true, prelaunch_simulator: true, result_bundle: true, @@ -143,7 +143,7 @@ lane :integration_tests do clear_derived_data() create_simulator_if_necessary( - name: "iPhone 16 Pro", + name: "iPhone 16 Pro (18.0)", type: "com.apple.CoreSimulator.SimDeviceType.iPhone-16-Pro", runtime: "com.apple.CoreSimulator.SimRuntime.iOS-18-0" ) @@ -152,7 +152,7 @@ lane :integration_tests do run_tests( scheme: "IntegrationTests", - destination: "platform=iOS Simulator,name=iPhone 16 Pro,OS=18.0", + device: "iPhone 16 Pro (18.0)", ensure_devices_found: true, result_bundle: true, reset_simulator: reset_simulator From 1347992cbc30a8e6be0ed037ee038e2df975a587 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Thu, 7 Nov 2024 10:10:17 +0200 Subject: [PATCH 112/114] Fix ElementCall UI test --- UITests/Sources/UserSessionScreenTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UITests/Sources/UserSessionScreenTests.swift b/UITests/Sources/UserSessionScreenTests.swift index 942dae1fd8..de6b232827 100644 --- a/UITests/Sources/UserSessionScreenTests.swift +++ b/UITests/Sources/UserSessionScreenTests.swift @@ -47,7 +47,7 @@ class UserSessionScreenTests: XCTestCase { let textField = app.textFields["Display name"] XCTAssert(textField.waitForExistence(timeout: 10)) - let joinButton = app.buttons["Join call now"] + let joinButton = app.buttons["Continue"] XCTAssert(joinButton.waitForExistence(timeout: 10)) } } From e4f346cbc76f872d95ecb1ce3288e5d9aa1c4441 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Thu, 7 Nov 2024 10:54:12 +0200 Subject: [PATCH 113/114] Fix useSessionScreen test snapshots. --- UITests/Sources/UserSessionScreenTests.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UITests/Sources/UserSessionScreenTests.swift b/UITests/Sources/UserSessionScreenTests.swift index de6b232827..7b932236ca 100644 --- a/UITests/Sources/UserSessionScreenTests.swift +++ b/UITests/Sources/UserSessionScreenTests.swift @@ -13,6 +13,9 @@ class UserSessionScreenTests: XCTestCase { func testUserSessionFlows() async throws { let app = Application.launch(.userSessionScreen) + + app.swipeDown() // Make sure the header shows a large title + try await app.assertScreenshot(.userSessionScreen, step: 1) app.buttons[A11yIdentifiers.homeScreen.roomName(firstRoomName)].tap() From a5cef531284be744e29530b334c3bf0147ff79ba Mon Sep 17 00:00:00 2001 From: Mauro <34335419+Velin92@users.noreply.github.com> Date: Thu, 7 Nov 2024 15:40:34 +0100 Subject: [PATCH 114/114] Update SDK 1.0.65 (#3494) * update SDK * use send queue should be false --- ElementX.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- .../Mocks/Generated/SDKGeneratedMocks.swift | 400 ++++++++++++------ .../Services/Timeline/TimelineProxy.swift | 20 +- project.yml | 2 +- 5 files changed, 289 insertions(+), 139 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 3184afc21c..715b05cf70 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -7858,7 +7858,7 @@ repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.0.64; + version = 1.0.65; }; }; 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index b336c80344..4cd886738e 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/matrix-rust-components-swift", "state" : { - "revision" : "5b2a50a75fe5814e5be4c550717d0e8a1abdbc7b", - "version" : "1.0.64" + "revision" : "399cc70987856c73e24b8888ac1ecc0eecf1716b", + "version" : "1.0.65" } }, { diff --git a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift index 94918f2205..43a24c5c6f 100644 --- a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift @@ -2056,6 +2056,81 @@ open class ClientSDKMock: MatrixRustSDK.Client { } } + //MARK: - isRoomAliasAvailable + + open var isRoomAliasAvailableAliasThrowableError: Error? + var isRoomAliasAvailableAliasUnderlyingCallsCount = 0 + open var isRoomAliasAvailableAliasCallsCount: Int { + get { + if Thread.isMainThread { + return isRoomAliasAvailableAliasUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = isRoomAliasAvailableAliasUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + isRoomAliasAvailableAliasUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + isRoomAliasAvailableAliasUnderlyingCallsCount = newValue + } + } + } + } + open var isRoomAliasAvailableAliasCalled: Bool { + return isRoomAliasAvailableAliasCallsCount > 0 + } + open var isRoomAliasAvailableAliasReceivedAlias: String? + open var isRoomAliasAvailableAliasReceivedInvocations: [String] = [] + + var isRoomAliasAvailableAliasUnderlyingReturnValue: Bool! + open var isRoomAliasAvailableAliasReturnValue: Bool! { + get { + if Thread.isMainThread { + return isRoomAliasAvailableAliasUnderlyingReturnValue + } else { + var returnValue: Bool? = nil + DispatchQueue.main.sync { + returnValue = isRoomAliasAvailableAliasUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + isRoomAliasAvailableAliasUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + isRoomAliasAvailableAliasUnderlyingReturnValue = newValue + } + } + } + } + open var isRoomAliasAvailableAliasClosure: ((String) async throws -> Bool)? + + open override func isRoomAliasAvailable(alias: String) async throws -> Bool { + if let error = isRoomAliasAvailableAliasThrowableError { + throw error + } + isRoomAliasAvailableAliasCallsCount += 1 + isRoomAliasAvailableAliasReceivedAlias = alias + DispatchQueue.main.async { + self.isRoomAliasAvailableAliasReceivedInvocations.append(alias) + } + if let isRoomAliasAvailableAliasClosure = isRoomAliasAvailableAliasClosure { + return try await isRoomAliasAvailableAliasClosure(alias) + } else { + return isRoomAliasAvailableAliasReturnValue + } + } + //MARK: - joinRoomById open var joinRoomByIdRoomIdThrowableError: Error? @@ -2676,13 +2751,13 @@ open class ClientSDKMock: MatrixRustSDK.Client { open var resolveRoomAliasRoomAliasReceivedRoomAlias: String? open var resolveRoomAliasRoomAliasReceivedInvocations: [String] = [] - var resolveRoomAliasRoomAliasUnderlyingReturnValue: ResolvedRoomAlias! - open var resolveRoomAliasRoomAliasReturnValue: ResolvedRoomAlias! { + var resolveRoomAliasRoomAliasUnderlyingReturnValue: ResolvedRoomAlias? + open var resolveRoomAliasRoomAliasReturnValue: ResolvedRoomAlias? { get { if Thread.isMainThread { return resolveRoomAliasRoomAliasUnderlyingReturnValue } else { - var returnValue: ResolvedRoomAlias? = nil + var returnValue: ResolvedRoomAlias?? = nil DispatchQueue.main.sync { returnValue = resolveRoomAliasRoomAliasUnderlyingReturnValue } @@ -2700,9 +2775,9 @@ open class ClientSDKMock: MatrixRustSDK.Client { } } } - open var resolveRoomAliasRoomAliasClosure: ((String) async throws -> ResolvedRoomAlias)? + open var resolveRoomAliasRoomAliasClosure: ((String) async throws -> ResolvedRoomAlias?)? - open override func resolveRoomAlias(roomAlias: String) async throws -> ResolvedRoomAlias { + open override func resolveRoomAlias(roomAlias: String) async throws -> ResolvedRoomAlias? { if let error = resolveRoomAliasRoomAliasThrowableError { throw error } @@ -2764,6 +2839,81 @@ open class ClientSDKMock: MatrixRustSDK.Client { try await restoreSessionSessionClosure?(session) } + //MARK: - roomAliasExists + + open var roomAliasExistsRoomAliasThrowableError: Error? + var roomAliasExistsRoomAliasUnderlyingCallsCount = 0 + open var roomAliasExistsRoomAliasCallsCount: Int { + get { + if Thread.isMainThread { + return roomAliasExistsRoomAliasUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = roomAliasExistsRoomAliasUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + roomAliasExistsRoomAliasUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + roomAliasExistsRoomAliasUnderlyingCallsCount = newValue + } + } + } + } + open var roomAliasExistsRoomAliasCalled: Bool { + return roomAliasExistsRoomAliasCallsCount > 0 + } + open var roomAliasExistsRoomAliasReceivedRoomAlias: String? + open var roomAliasExistsRoomAliasReceivedInvocations: [String] = [] + + var roomAliasExistsRoomAliasUnderlyingReturnValue: Bool! + open var roomAliasExistsRoomAliasReturnValue: Bool! { + get { + if Thread.isMainThread { + return roomAliasExistsRoomAliasUnderlyingReturnValue + } else { + var returnValue: Bool? = nil + DispatchQueue.main.sync { + returnValue = roomAliasExistsRoomAliasUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + roomAliasExistsRoomAliasUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + roomAliasExistsRoomAliasUnderlyingReturnValue = newValue + } + } + } + } + open var roomAliasExistsRoomAliasClosure: ((String) async throws -> Bool)? + + open override func roomAliasExists(roomAlias: String) async throws -> Bool { + if let error = roomAliasExistsRoomAliasThrowableError { + throw error + } + roomAliasExistsRoomAliasCallsCount += 1 + roomAliasExistsRoomAliasReceivedRoomAlias = roomAlias + DispatchQueue.main.async { + self.roomAliasExistsRoomAliasReceivedInvocations.append(roomAlias) + } + if let roomAliasExistsRoomAliasClosure = roomAliasExistsRoomAliasClosure { + return try await roomAliasExistsRoomAliasClosure(roomAlias) + } else { + return roomAliasExistsRoomAliasReturnValue + } + } + //MARK: - roomDirectorySearch var roomDirectorySearchUnderlyingCallsCount = 0 @@ -19047,15 +19197,15 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { //MARK: - sendAudio - var sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = 0 - open var sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount: Int { + var sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount = 0 + open var sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueCallsCount: Int { get { if Thread.isMainThread { - return sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount + return sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount + returnValue = sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount } return returnValue! @@ -19063,29 +19213,29 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = newValue + sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = newValue + sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount = newValue } } } } - open var sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCalled: Bool { - return sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount > 0 + open var sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueCalled: Bool { + return sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueCallsCount > 0 } - open var sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedArguments: (url: String, audioInfo: AudioInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?)? - open var sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedInvocations: [(url: String, audioInfo: AudioInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?)] = [] + open var sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReceivedArguments: (url: String, audioInfo: AudioInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?, useSendQueue: Bool)? + open var sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReceivedInvocations: [(url: String, audioInfo: AudioInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?, useSendQueue: Bool)] = [] - var sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue: SendAttachmentJoinHandle! - open var sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReturnValue: SendAttachmentJoinHandle! { + var sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue: SendAttachmentJoinHandle! + open var sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReturnValue: SendAttachmentJoinHandle! { get { if Thread.isMainThread { - return sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue + return sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue } else { var returnValue: SendAttachmentJoinHandle? = nil DispatchQueue.main.sync { - returnValue = sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue + returnValue = sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue } return returnValue! @@ -19093,40 +19243,40 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue = newValue + sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue = newValue + sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue = newValue } } } } - open var sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure: ((String, AudioInfo, String?, FormattedBody?, Bool, ProgressWatcher?) -> SendAttachmentJoinHandle)? + open var sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueClosure: ((String, AudioInfo, String?, FormattedBody?, ProgressWatcher?, Bool) -> SendAttachmentJoinHandle)? - open override func sendAudio(url: String, audioInfo: AudioInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?) -> SendAttachmentJoinHandle { - sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount += 1 - sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedArguments = (url: url, audioInfo: audioInfo, caption: caption, formattedCaption: formattedCaption, storeInCache: storeInCache, progressWatcher: progressWatcher) + open override func sendAudio(url: String, audioInfo: AudioInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?, useSendQueue: Bool) -> SendAttachmentJoinHandle { + sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueCallsCount += 1 + sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReceivedArguments = (url: url, audioInfo: audioInfo, caption: caption, formattedCaption: formattedCaption, progressWatcher: progressWatcher, useSendQueue: useSendQueue) DispatchQueue.main.async { - self.sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedInvocations.append((url: url, audioInfo: audioInfo, caption: caption, formattedCaption: formattedCaption, storeInCache: storeInCache, progressWatcher: progressWatcher)) + self.sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReceivedInvocations.append((url: url, audioInfo: audioInfo, caption: caption, formattedCaption: formattedCaption, progressWatcher: progressWatcher, useSendQueue: useSendQueue)) } - if let sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure = sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure { - return sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure(url, audioInfo, caption, formattedCaption, storeInCache, progressWatcher) + if let sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueClosure = sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueClosure { + return sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueClosure(url, audioInfo, caption, formattedCaption, progressWatcher, useSendQueue) } else { - return sendAudioUrlAudioInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReturnValue + return sendAudioUrlAudioInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReturnValue } } //MARK: - sendFile - var sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingCallsCount = 0 - open var sendFileUrlFileInfoStoreInCacheProgressWatcherCallsCount: Int { + var sendFileUrlFileInfoProgressWatcherUseSendQueueUnderlyingCallsCount = 0 + open var sendFileUrlFileInfoProgressWatcherUseSendQueueCallsCount: Int { get { if Thread.isMainThread { - return sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingCallsCount + return sendFileUrlFileInfoProgressWatcherUseSendQueueUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingCallsCount + returnValue = sendFileUrlFileInfoProgressWatcherUseSendQueueUnderlyingCallsCount } return returnValue! @@ -19134,29 +19284,29 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingCallsCount = newValue + sendFileUrlFileInfoProgressWatcherUseSendQueueUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingCallsCount = newValue + sendFileUrlFileInfoProgressWatcherUseSendQueueUnderlyingCallsCount = newValue } } } } - open var sendFileUrlFileInfoStoreInCacheProgressWatcherCalled: Bool { - return sendFileUrlFileInfoStoreInCacheProgressWatcherCallsCount > 0 + open var sendFileUrlFileInfoProgressWatcherUseSendQueueCalled: Bool { + return sendFileUrlFileInfoProgressWatcherUseSendQueueCallsCount > 0 } - open var sendFileUrlFileInfoStoreInCacheProgressWatcherReceivedArguments: (url: String, fileInfo: FileInfo, storeInCache: Bool, progressWatcher: ProgressWatcher?)? - open var sendFileUrlFileInfoStoreInCacheProgressWatcherReceivedInvocations: [(url: String, fileInfo: FileInfo, storeInCache: Bool, progressWatcher: ProgressWatcher?)] = [] + open var sendFileUrlFileInfoProgressWatcherUseSendQueueReceivedArguments: (url: String, fileInfo: FileInfo, progressWatcher: ProgressWatcher?, useSendQueue: Bool)? + open var sendFileUrlFileInfoProgressWatcherUseSendQueueReceivedInvocations: [(url: String, fileInfo: FileInfo, progressWatcher: ProgressWatcher?, useSendQueue: Bool)] = [] - var sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingReturnValue: SendAttachmentJoinHandle! - open var sendFileUrlFileInfoStoreInCacheProgressWatcherReturnValue: SendAttachmentJoinHandle! { + var sendFileUrlFileInfoProgressWatcherUseSendQueueUnderlyingReturnValue: SendAttachmentJoinHandle! + open var sendFileUrlFileInfoProgressWatcherUseSendQueueReturnValue: SendAttachmentJoinHandle! { get { if Thread.isMainThread { - return sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingReturnValue + return sendFileUrlFileInfoProgressWatcherUseSendQueueUnderlyingReturnValue } else { var returnValue: SendAttachmentJoinHandle? = nil DispatchQueue.main.sync { - returnValue = sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingReturnValue + returnValue = sendFileUrlFileInfoProgressWatcherUseSendQueueUnderlyingReturnValue } return returnValue! @@ -19164,40 +19314,40 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingReturnValue = newValue + sendFileUrlFileInfoProgressWatcherUseSendQueueUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - sendFileUrlFileInfoStoreInCacheProgressWatcherUnderlyingReturnValue = newValue + sendFileUrlFileInfoProgressWatcherUseSendQueueUnderlyingReturnValue = newValue } } } } - open var sendFileUrlFileInfoStoreInCacheProgressWatcherClosure: ((String, FileInfo, Bool, ProgressWatcher?) -> SendAttachmentJoinHandle)? + open var sendFileUrlFileInfoProgressWatcherUseSendQueueClosure: ((String, FileInfo, ProgressWatcher?, Bool) -> SendAttachmentJoinHandle)? - open override func sendFile(url: String, fileInfo: FileInfo, storeInCache: Bool, progressWatcher: ProgressWatcher?) -> SendAttachmentJoinHandle { - sendFileUrlFileInfoStoreInCacheProgressWatcherCallsCount += 1 - sendFileUrlFileInfoStoreInCacheProgressWatcherReceivedArguments = (url: url, fileInfo: fileInfo, storeInCache: storeInCache, progressWatcher: progressWatcher) + open override func sendFile(url: String, fileInfo: FileInfo, progressWatcher: ProgressWatcher?, useSendQueue: Bool) -> SendAttachmentJoinHandle { + sendFileUrlFileInfoProgressWatcherUseSendQueueCallsCount += 1 + sendFileUrlFileInfoProgressWatcherUseSendQueueReceivedArguments = (url: url, fileInfo: fileInfo, progressWatcher: progressWatcher, useSendQueue: useSendQueue) DispatchQueue.main.async { - self.sendFileUrlFileInfoStoreInCacheProgressWatcherReceivedInvocations.append((url: url, fileInfo: fileInfo, storeInCache: storeInCache, progressWatcher: progressWatcher)) + self.sendFileUrlFileInfoProgressWatcherUseSendQueueReceivedInvocations.append((url: url, fileInfo: fileInfo, progressWatcher: progressWatcher, useSendQueue: useSendQueue)) } - if let sendFileUrlFileInfoStoreInCacheProgressWatcherClosure = sendFileUrlFileInfoStoreInCacheProgressWatcherClosure { - return sendFileUrlFileInfoStoreInCacheProgressWatcherClosure(url, fileInfo, storeInCache, progressWatcher) + if let sendFileUrlFileInfoProgressWatcherUseSendQueueClosure = sendFileUrlFileInfoProgressWatcherUseSendQueueClosure { + return sendFileUrlFileInfoProgressWatcherUseSendQueueClosure(url, fileInfo, progressWatcher, useSendQueue) } else { - return sendFileUrlFileInfoStoreInCacheProgressWatcherReturnValue + return sendFileUrlFileInfoProgressWatcherUseSendQueueReturnValue } } //MARK: - sendImage - var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = 0 - open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount: Int { + var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount = 0 + open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueCallsCount: Int { get { if Thread.isMainThread { - return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount + return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount + returnValue = sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount } return returnValue! @@ -19205,29 +19355,29 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = newValue + sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = newValue + sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount = newValue } } } } - open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCalled: Bool { - return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount > 0 + open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueCalled: Bool { + return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueCallsCount > 0 } - open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedArguments: (url: String, thumbnailUrl: String?, imageInfo: ImageInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?)? - open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedInvocations: [(url: String, thumbnailUrl: String?, imageInfo: ImageInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?)] = [] + open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReceivedArguments: (url: String, thumbnailUrl: String?, imageInfo: ImageInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?, useSendQueue: Bool)? + open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReceivedInvocations: [(url: String, thumbnailUrl: String?, imageInfo: ImageInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?, useSendQueue: Bool)] = [] - var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue: SendAttachmentJoinHandle! - open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReturnValue: SendAttachmentJoinHandle! { + var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue: SendAttachmentJoinHandle! + open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReturnValue: SendAttachmentJoinHandle! { get { if Thread.isMainThread { - return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue + return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue } else { var returnValue: SendAttachmentJoinHandle? = nil DispatchQueue.main.sync { - returnValue = sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue + returnValue = sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue } return returnValue! @@ -19235,26 +19385,26 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue = newValue + sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue = newValue + sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue = newValue } } } } - open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure: ((String, String?, ImageInfo, String?, FormattedBody?, Bool, ProgressWatcher?) -> SendAttachmentJoinHandle)? + open var sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueClosure: ((String, String?, ImageInfo, String?, FormattedBody?, ProgressWatcher?, Bool) -> SendAttachmentJoinHandle)? - open override func sendImage(url: String, thumbnailUrl: String?, imageInfo: ImageInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?) -> SendAttachmentJoinHandle { - sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount += 1 - sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedArguments = (url: url, thumbnailUrl: thumbnailUrl, imageInfo: imageInfo, caption: caption, formattedCaption: formattedCaption, storeInCache: storeInCache, progressWatcher: progressWatcher) + open override func sendImage(url: String, thumbnailUrl: String?, imageInfo: ImageInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?, useSendQueue: Bool) -> SendAttachmentJoinHandle { + sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueCallsCount += 1 + sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReceivedArguments = (url: url, thumbnailUrl: thumbnailUrl, imageInfo: imageInfo, caption: caption, formattedCaption: formattedCaption, progressWatcher: progressWatcher, useSendQueue: useSendQueue) DispatchQueue.main.async { - self.sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedInvocations.append((url: url, thumbnailUrl: thumbnailUrl, imageInfo: imageInfo, caption: caption, formattedCaption: formattedCaption, storeInCache: storeInCache, progressWatcher: progressWatcher)) + self.sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReceivedInvocations.append((url: url, thumbnailUrl: thumbnailUrl, imageInfo: imageInfo, caption: caption, formattedCaption: formattedCaption, progressWatcher: progressWatcher, useSendQueue: useSendQueue)) } - if let sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure = sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure { - return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure(url, thumbnailUrl, imageInfo, caption, formattedCaption, storeInCache, progressWatcher) + if let sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueClosure = sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueClosure { + return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueClosure(url, thumbnailUrl, imageInfo, caption, formattedCaption, progressWatcher, useSendQueue) } else { - return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReturnValue + return sendImageUrlThumbnailUrlImageInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReturnValue } } @@ -19440,15 +19590,15 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { //MARK: - sendVideo - var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = 0 - open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount: Int { + var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount = 0 + open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueCallsCount: Int { get { if Thread.isMainThread { - return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount + return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount + returnValue = sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount } return returnValue! @@ -19456,29 +19606,29 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = newValue + sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = newValue + sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount = newValue } } } } - open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCalled: Bool { - return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount > 0 + open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueCalled: Bool { + return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueCallsCount > 0 } - open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedArguments: (url: String, thumbnailUrl: String?, videoInfo: VideoInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?)? - open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedInvocations: [(url: String, thumbnailUrl: String?, videoInfo: VideoInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?)] = [] + open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReceivedArguments: (url: String, thumbnailUrl: String?, videoInfo: VideoInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?, useSendQueue: Bool)? + open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReceivedInvocations: [(url: String, thumbnailUrl: String?, videoInfo: VideoInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?, useSendQueue: Bool)] = [] - var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue: SendAttachmentJoinHandle! - open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReturnValue: SendAttachmentJoinHandle! { + var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue: SendAttachmentJoinHandle! + open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReturnValue: SendAttachmentJoinHandle! { get { if Thread.isMainThread { - return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue + return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue } else { var returnValue: SendAttachmentJoinHandle? = nil DispatchQueue.main.sync { - returnValue = sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue + returnValue = sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue } return returnValue! @@ -19486,40 +19636,40 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue = newValue + sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue = newValue + sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue = newValue } } } } - open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure: ((String, String?, VideoInfo, String?, FormattedBody?, Bool, ProgressWatcher?) -> SendAttachmentJoinHandle)? + open var sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueClosure: ((String, String?, VideoInfo, String?, FormattedBody?, ProgressWatcher?, Bool) -> SendAttachmentJoinHandle)? - open override func sendVideo(url: String, thumbnailUrl: String?, videoInfo: VideoInfo, caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?) -> SendAttachmentJoinHandle { - sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount += 1 - sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedArguments = (url: url, thumbnailUrl: thumbnailUrl, videoInfo: videoInfo, caption: caption, formattedCaption: formattedCaption, storeInCache: storeInCache, progressWatcher: progressWatcher) + open override func sendVideo(url: String, thumbnailUrl: String?, videoInfo: VideoInfo, caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?, useSendQueue: Bool) -> SendAttachmentJoinHandle { + sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueCallsCount += 1 + sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReceivedArguments = (url: url, thumbnailUrl: thumbnailUrl, videoInfo: videoInfo, caption: caption, formattedCaption: formattedCaption, progressWatcher: progressWatcher, useSendQueue: useSendQueue) DispatchQueue.main.async { - self.sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedInvocations.append((url: url, thumbnailUrl: thumbnailUrl, videoInfo: videoInfo, caption: caption, formattedCaption: formattedCaption, storeInCache: storeInCache, progressWatcher: progressWatcher)) + self.sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReceivedInvocations.append((url: url, thumbnailUrl: thumbnailUrl, videoInfo: videoInfo, caption: caption, formattedCaption: formattedCaption, progressWatcher: progressWatcher, useSendQueue: useSendQueue)) } - if let sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure = sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure { - return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherClosure(url, thumbnailUrl, videoInfo, caption, formattedCaption, storeInCache, progressWatcher) + if let sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueClosure = sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueClosure { + return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueClosure(url, thumbnailUrl, videoInfo, caption, formattedCaption, progressWatcher, useSendQueue) } else { - return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionStoreInCacheProgressWatcherReturnValue + return sendVideoUrlThumbnailUrlVideoInfoCaptionFormattedCaptionProgressWatcherUseSendQueueReturnValue } } //MARK: - sendVoiceMessage - var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = 0 - open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount: Int { + var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount = 0 + open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueCallsCount: Int { get { if Thread.isMainThread { - return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount + return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount + returnValue = sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount } return returnValue! @@ -19527,29 +19677,29 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = newValue + sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingCallsCount = newValue + sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingCallsCount = newValue } } } } - open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherCalled: Bool { - return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount > 0 + open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueCalled: Bool { + return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueCallsCount > 0 } - open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedArguments: (url: String, audioInfo: AudioInfo, waveform: [UInt16], caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?)? - open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedInvocations: [(url: String, audioInfo: AudioInfo, waveform: [UInt16], caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?)] = [] + open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueReceivedArguments: (url: String, audioInfo: AudioInfo, waveform: [UInt16], caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?, useSendQueue: Bool)? + open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueReceivedInvocations: [(url: String, audioInfo: AudioInfo, waveform: [UInt16], caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?, useSendQueue: Bool)] = [] - var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue: SendAttachmentJoinHandle! - open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherReturnValue: SendAttachmentJoinHandle! { + var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue: SendAttachmentJoinHandle! + open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueReturnValue: SendAttachmentJoinHandle! { get { if Thread.isMainThread { - return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue + return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue } else { var returnValue: SendAttachmentJoinHandle? = nil DispatchQueue.main.sync { - returnValue = sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue + returnValue = sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue } return returnValue! @@ -19557,26 +19707,26 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } set { if Thread.isMainThread { - sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue = newValue + sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherUnderlyingReturnValue = newValue + sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueUnderlyingReturnValue = newValue } } } } - open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherClosure: ((String, AudioInfo, [UInt16], String?, FormattedBody?, Bool, ProgressWatcher?) -> SendAttachmentJoinHandle)? + open var sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueClosure: ((String, AudioInfo, [UInt16], String?, FormattedBody?, ProgressWatcher?, Bool) -> SendAttachmentJoinHandle)? - open override func sendVoiceMessage(url: String, audioInfo: AudioInfo, waveform: [UInt16], caption: String?, formattedCaption: FormattedBody?, storeInCache: Bool, progressWatcher: ProgressWatcher?) -> SendAttachmentJoinHandle { - sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherCallsCount += 1 - sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedArguments = (url: url, audioInfo: audioInfo, waveform: waveform, caption: caption, formattedCaption: formattedCaption, storeInCache: storeInCache, progressWatcher: progressWatcher) + open override func sendVoiceMessage(url: String, audioInfo: AudioInfo, waveform: [UInt16], caption: String?, formattedCaption: FormattedBody?, progressWatcher: ProgressWatcher?, useSendQueue: Bool) -> SendAttachmentJoinHandle { + sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueCallsCount += 1 + sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueReceivedArguments = (url: url, audioInfo: audioInfo, waveform: waveform, caption: caption, formattedCaption: formattedCaption, progressWatcher: progressWatcher, useSendQueue: useSendQueue) DispatchQueue.main.async { - self.sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherReceivedInvocations.append((url: url, audioInfo: audioInfo, waveform: waveform, caption: caption, formattedCaption: formattedCaption, storeInCache: storeInCache, progressWatcher: progressWatcher)) + self.sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueReceivedInvocations.append((url: url, audioInfo: audioInfo, waveform: waveform, caption: caption, formattedCaption: formattedCaption, progressWatcher: progressWatcher, useSendQueue: useSendQueue)) } - if let sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherClosure = sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherClosure { - return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherClosure(url, audioInfo, waveform, caption, formattedCaption, storeInCache, progressWatcher) + if let sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueClosure = sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueClosure { + return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueClosure(url, audioInfo, waveform, caption, formattedCaption, progressWatcher, useSendQueue) } else { - return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionStoreInCacheProgressWatcherReturnValue + return sendVoiceMessageUrlAudioInfoWaveformCaptionFormattedCaptionProgressWatcherUseSendQueueReturnValue } } diff --git a/ElementX/Sources/Services/Timeline/TimelineProxy.swift b/ElementX/Sources/Services/Timeline/TimelineProxy.swift index 2453bada76..64b5152d69 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxy.swift @@ -231,10 +231,10 @@ final class TimelineProxy: TimelineProxyProtocol { audioInfo: audioInfo, caption: nil, formattedCaption: nil, - storeInCache: true, progressWatcher: UploadProgressListener { progress in progressSubject?.send(progress) - }) + }, + useSendQueue: false) await requestHandle(handle) @@ -257,10 +257,10 @@ final class TimelineProxy: TimelineProxyProtocol { let handle = timeline.sendFile(url: url.path(percentEncoded: false), fileInfo: fileInfo, - storeInCache: true, progressWatcher: UploadProgressListener { progress in progressSubject?.send(progress) - }) + }, + useSendQueue: false) await requestHandle(handle) @@ -287,10 +287,10 @@ final class TimelineProxy: TimelineProxyProtocol { imageInfo: imageInfo, caption: nil, formattedCaption: nil, - storeInCache: true, progressWatcher: UploadProgressListener { progress in progressSubject?.send(progress) - }) + }, + useSendQueue: false) await requestHandle(handle) @@ -335,10 +335,10 @@ final class TimelineProxy: TimelineProxyProtocol { videoInfo: videoInfo, caption: nil, formattedCaption: nil, - storeInCache: true, progressWatcher: UploadProgressListener { progress in progressSubject?.send(progress) - }) + }, + useSendQueue: false) await requestHandle(handle) @@ -365,10 +365,10 @@ final class TimelineProxy: TimelineProxyProtocol { waveform: waveform, caption: nil, formattedCaption: nil, - storeInCache: true, progressWatcher: UploadProgressListener { progress in progressSubject?.send(progress) - }) + }, + useSendQueue: false) await requestHandle(handle) diff --git a/project.yml b/project.yml index b7ee5742e4..4c0b181f7b 100644 --- a/project.yml +++ b/project.yml @@ -60,7 +60,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/element-hq/matrix-rust-components-swift - exactVersion: 1.0.64 + exactVersion: 1.0.65 # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios