From d34c38d48a00095c1388931222f7a14a74edbcd8 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Wed, 8 Apr 2026 11:03:30 +0100 Subject: [PATCH] Add a view with instructions for enabling backups in Element Classic. (#5359) No presentation logic yet. --- .../Sources/GeneratedAccessibilityTests.swift | 4 + ElementX.xcodeproj/project.pbxproj | 4 + .../TestablePreviewsDictionary.swift | 1 + .../AuthenticationStartScreenModels.swift | 1 + ...tionClassicAppBackupInstructionsView.swift | 74 +++++++++++++++++++ .../View/AuthenticationStartScreen.swift | 3 + .../Sources/GeneratedPreviewTests.swift | 8 ++ ...AppBackupInstructionsView.iPad-en-GB-0.png | 3 + ...ppBackupInstructionsView.iPad-pseudo-0.png | 3 + ...pBackupInstructionsView.iPhone-en-GB-0.png | 3 + ...BackupInstructionsView.iPhone-pseudo-0.png | 3 + 11 files changed, 107 insertions(+) create mode 100644 ElementX/Sources/Screens/Authentication/StartScreen/View/AuthenticationClassicAppBackupInstructionsView.swift create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationClassicAppBackupInstructionsView.iPad-en-GB-0.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationClassicAppBackupInstructionsView.iPad-pseudo-0.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationClassicAppBackupInstructionsView.iPhone-en-GB-0.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationClassicAppBackupInstructionsView.iPhone-pseudo-0.png diff --git a/AccessibilityTests/Sources/GeneratedAccessibilityTests.swift b/AccessibilityTests/Sources/GeneratedAccessibilityTests.swift index 099c7f1d3..f441c3076 100644 --- a/AccessibilityTests/Sources/GeneratedAccessibilityTests.swift +++ b/AccessibilityTests/Sources/GeneratedAccessibilityTests.swift @@ -43,6 +43,10 @@ extension AccessibilityTests { try await performAccessibilityAudit(named: "AudioRoomTimelineView_Previews") } + func testAuthenticationClassicAppBackupInstructionsView() async throws { + try await performAccessibilityAudit(named: "AuthenticationClassicAppBackupInstructionsView_Previews") + } + func testAuthenticationStartScreen() async throws { try await performAccessibilityAudit(named: "AuthenticationStartScreen_Previews") } diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index ca86f23a7..ecb783cb7 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -846,6 +846,7 @@ 8F3AD08F2E706AA60F1A1D4D /* portrait_test_image.jpg in Resources */ = {isa = PBXBuildFile; fileRef = BC51BF90469412ABDE658CDD /* portrait_test_image.jpg */; }; 904F06C9C1AEF884C2077542 /* RoomDirectorySearchScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2E4EF80DFB8FE7C4469B15D /* RoomDirectorySearchScreen.swift */; }; 90733645AE76FB33DAD28C2B /* URLSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE40D4A5DD857AC16EED945A /* URLSession.swift */; }; + 90C683C87BF6D39419402E5B /* AuthenticationClassicAppBackupInstructionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B0E0A2F16603E7C827C295 /* AuthenticationClassicAppBackupInstructionsView.swift */; }; 90DF83A6A347F7EE7EDE89EE /* AttributedStringBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF25E364AE85090A70AE4644 /* AttributedStringBuilderTests.swift */; }; 90EB25D13AE6EEF034BDE9D2 /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D52BAA5BADB06E5E8C295D /* Assets.swift */; }; 914BDF61447C723F104BCE33 /* SessionDirectories.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C2067FF58B4996323EB40C /* SessionDirectories.swift */; }; @@ -2747,6 +2748,7 @@ D1D97BAF04AA150C0EF03021 /* VerificationBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerificationBadge.swift; sourceTree = ""; }; D263254AFE5B7993FFBBF324 /* NSE.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NSE.entitlements; sourceTree = ""; }; D28F7A6CEEA4A2815B0F0F55 /* SettingsFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFlowCoordinator.swift; sourceTree = ""; }; + D2B0E0A2F16603E7C827C295 /* AuthenticationClassicAppBackupInstructionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationClassicAppBackupInstructionsView.swift; sourceTree = ""; }; D2C513A6CD99E6C3C163DA1E /* RowDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RowDivider.swift; sourceTree = ""; }; D316BB02636AF2174F2580E6 /* SoftLogoutScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutScreenViewModelProtocol.swift; sourceTree = ""; }; D33116993D54FADC0C721C1F /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = ""; }; @@ -5703,6 +5705,7 @@ A18A0DA2E598A7E76C50E53D /* View */ = { isa = PBXGroup; children = ( + D2B0E0A2F16603E7C827C295 /* AuthenticationClassicAppBackupInstructionsView.swift */, 98784280D98C852727BE0111 /* AuthenticationStartLogo.swift */, A768CA51A59B8A5D8C8FD599 /* AuthenticationStartScreen.swift */, 682BC7BAF0EFEF512A8C5140 /* AuthenticationStartScreenBackgroundImage.swift */, @@ -8043,6 +8046,7 @@ 88F348E2CB14FF71CBBB665D /* AudioRoomTimelineItemContent.swift in Sources */, 7BD2123144A32F082CECC108 /* AudioRoomTimelineView.swift in Sources */, 9278EC51D24E57445B290521 /* AudioSessionProtocol.swift in Sources */, + 90C683C87BF6D39419402E5B /* AuthenticationClassicAppBackupInstructionsView.swift in Sources */, A51C65E5A3C9F2464A91A380 /* AuthenticationClientBuilderFactoryMock.swift in Sources */, AE066FC93E7B707C826B335A /* AuthenticationClientFactory.swift in Sources */, 67E9926C4572C54F59FCA91A /* AuthenticationFlowCoordinator.swift in Sources */, diff --git a/ElementX/Sources/Other/TestablePreview/TestablePreviewsDictionary.swift b/ElementX/Sources/Other/TestablePreview/TestablePreviewsDictionary.swift index 0e4b0add5..1c9ce53c7 100644 --- a/ElementX/Sources/Other/TestablePreview/TestablePreviewsDictionary.swift +++ b/ElementX/Sources/Other/TestablePreview/TestablePreviewsDictionary.swift @@ -18,6 +18,7 @@ enum TestablePreviewsDictionary { "AppLockSetupSettingsScreen_Previews" : AppLockSetupSettingsScreen_Previews.self, "AudioMediaEventsTimelineView_Previews" : AudioMediaEventsTimelineView_Previews.self, "AudioRoomTimelineView_Previews" : AudioRoomTimelineView_Previews.self, + "AuthenticationClassicAppBackupInstructionsView_Previews" : AuthenticationClassicAppBackupInstructionsView_Previews.self, "AuthenticationStartScreen_Previews" : AuthenticationStartScreen_Previews.self, "AvatarHeaderView_Previews" : AvatarHeaderView_Previews.self, "BadgeLabel_Previews" : BadgeLabel_Previews.self, diff --git a/ElementX/Sources/Screens/Authentication/StartScreen/AuthenticationStartScreenModels.swift b/ElementX/Sources/Screens/Authentication/StartScreen/AuthenticationStartScreenModels.swift index c214acfd5..57607cec4 100644 --- a/ElementX/Sources/Screens/Authentication/StartScreen/AuthenticationStartScreenModels.swift +++ b/ElementX/Sources/Screens/Authentication/StartScreen/AuthenticationStartScreenModels.swift @@ -55,6 +55,7 @@ struct AuthenticationStartScreenViewState: BindableState { struct AuthenticationStartScreenViewStateBindings { var alertInfo: AlertInfo? + var showClassicAppBackupInstructions = false } enum AuthenticationStartScreenAlertType { diff --git a/ElementX/Sources/Screens/Authentication/StartScreen/View/AuthenticationClassicAppBackupInstructionsView.swift b/ElementX/Sources/Screens/Authentication/StartScreen/View/AuthenticationClassicAppBackupInstructionsView.swift new file mode 100644 index 000000000..93b065845 --- /dev/null +++ b/ElementX/Sources/Screens/Authentication/StartScreen/View/AuthenticationClassicAppBackupInstructionsView.swift @@ -0,0 +1,74 @@ +// +// Copyright 2026 Element Creations Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. +// Please see LICENSE files in the repository root for full details. +// + +import Compound +import SwiftUI + +struct AuthenticationClassicAppBackupInstructionsView: View { + let context: AuthenticationStartScreenViewModel.Context + + @Environment(\.dismiss) private var dismiss + + var body: some View { + ElementNavigationStack { + FullscreenDialog(topPadding: 24, horizontalPadding: 24) { + content + } bottomContent: { + buttons + } + .navigationBarTitleDisplayMode(.inline) + .toolbar { toolbar } + .background() + .backgroundStyle(.compound.bgSubtleSecondary) + } + } + + var content: some View { + VStack(alignment: .leading, spacing: 40) { + TitleAndIcon(title: L10n.screenMissingKeyBackupTitle(InfoPlistReader.main.bundleDisplayName), + icon: \.keySolid, + iconStyle: .default) + + SFNumberedListView(items: [ + AttributedString(L10n.screenMissingKeyBackupStep1), + AttributedString(L10n.screenMissingKeyBackupStep2Ios), + AttributedString(L10n.screenMissingKeyBackupStep3Ios), + AttributedString(L10n.screenMissingKeyBackupStep4), + AttributedString(L10n.screenMissingKeyBackupStep5(InfoPlistReader.main.bundleDisplayName)) + ]) + } + } + + var buttons: some View { + Button(L10n.screenMissingKeyBackupOpenElementClassic) { + UIApplication.shared.open("element://open") + } + .buttonStyle(.compound(.primary)) + } + + var toolbar: some ToolbarContent { + ToolbarItem(placement: .primaryAction) { + ToolbarButton(role: .close, action: dismiss.callAsFunction) + } + } +} + +struct AuthenticationClassicAppBackupInstructionsView_Previews: PreviewProvider, TestablePreview { + static let viewModel = makeViewModel() + + static var previews: some View { + AuthenticationClassicAppBackupInstructionsView(context: viewModel.context) + } + + static func makeViewModel() -> AuthenticationStartScreenViewModel { + AuthenticationStartScreenViewModel(authenticationService: AuthenticationService.mock, + provisioningParameters: nil, + isBugReportServiceEnabled: false, + appSettings: ServiceLocator.shared.settings, + userIndicatorController: UserIndicatorControllerMock()) + } +} diff --git a/ElementX/Sources/Screens/Authentication/StartScreen/View/AuthenticationStartScreen.swift b/ElementX/Sources/Screens/Authentication/StartScreen/View/AuthenticationStartScreen.swift index e9fb1fa28..946a96493 100644 --- a/ElementX/Sources/Screens/Authentication/StartScreen/View/AuthenticationStartScreen.swift +++ b/ElementX/Sources/Screens/Authentication/StartScreen/View/AuthenticationStartScreen.swift @@ -46,6 +46,9 @@ struct AuthenticationStartScreen: View { AuthenticationStartScreenBackgroundImage() } .alert(item: $context.alertInfo) + .sheet(isPresented: $context.showClassicAppBackupInstructions) { + AuthenticationClassicAppBackupInstructionsView(context: context) + } .introspect(.window, on: .supportedVersions) { window in context.send(viewAction: .updateWindow(window)) } diff --git a/PreviewTests/Sources/GeneratedPreviewTests.swift b/PreviewTests/Sources/GeneratedPreviewTests.swift index 976fad120..4177c4c75 100644 --- a/PreviewTests/Sources/GeneratedPreviewTests.swift +++ b/PreviewTests/Sources/GeneratedPreviewTests.swift @@ -83,6 +83,14 @@ extension PreviewTests { } } + @Test + func authenticationClassicAppBackupInstructionsView() async throws { + AppSettings.resetAllSettings() // Ensure this test's previews start with fresh settings. + for (index, preview) in AuthenticationClassicAppBackupInstructionsView_Previews._allPreviews.enumerated() { + try await assertSnapshots(matching: preview, step: index) + } + } + @Test func authenticationStartScreen() async throws { AppSettings.resetAllSettings() // Ensure this test's previews start with fresh settings. diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationClassicAppBackupInstructionsView.iPad-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationClassicAppBackupInstructionsView.iPad-en-GB-0.png new file mode 100644 index 000000000..c787f220f --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationClassicAppBackupInstructionsView.iPad-en-GB-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d033dd37f0c28c175f1181404ea9855963ffe830394dabfbbaf4877f36e52a4 +size 145715 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationClassicAppBackupInstructionsView.iPad-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationClassicAppBackupInstructionsView.iPad-pseudo-0.png new file mode 100644 index 000000000..5374f3a72 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationClassicAppBackupInstructionsView.iPad-pseudo-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ffa8567fccd143f8dfe238c96d263717c99a6448953d3d10247b1012c751a956 +size 181186 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationClassicAppBackupInstructionsView.iPhone-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationClassicAppBackupInstructionsView.iPhone-en-GB-0.png new file mode 100644 index 000000000..d575939a3 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationClassicAppBackupInstructionsView.iPhone-en-GB-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c20b19a79a4287aca05cc58d51e09232861361a5d9f29606f8a7eb912e78d2ee +size 96938 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationClassicAppBackupInstructionsView.iPhone-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationClassicAppBackupInstructionsView.iPhone-pseudo-0.png new file mode 100644 index 000000000..18d11122b --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationClassicAppBackupInstructionsView.iPhone-pseudo-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ef97e734216be0e6ef36d0f0106c71728ea5a0cf57ce2164ad09157953dc22e2 +size 141919