Verify Element X with an existing Element Classic account. (#5374)

* Read and import the secrets from ClassicAppAccounts.

* Record snapshots.

* Add some documentation, tidy up tests and fix the dismissal of the backup instructions.

* Workaround flakey tests (the fulfilments weren't always firing).

* Allow a custom Classic App deep link URL to be configured.
This commit is contained in:
Doug
2026-04-13 15:30:09 +01:00
committed by GitHub
parent 001a389f51
commit 7c812201cb
40 changed files with 947 additions and 103 deletions
@@ -16,6 +16,15 @@ struct AuthenticationStartScreen: View {
@Bindable var context: AuthenticationStartScreenViewModel.Context
var body: some View {
if case let .welcomeBack(classicAppAccount) = context.viewState.classicAppMode,
classicAppAccount.state.isServerSupported != false {
AuthenticationClassicAppAccountView(context: context, classicAppAccount: classicAppAccount)
} else {
standardContent
}
}
var standardContent: some View {
// This view uses a GeometryReader instead of FullscreenDialog so its content takes the full
// height available (after taking the buttons out of the equation) in order for the logo
// and title to appear vertically centred and equally spaced within this content area.
@@ -41,14 +50,12 @@ struct AuthenticationStartScreen: View {
}
.scrollBounceBehavior(.basedOnSize)
}
.navigationBarHidden(true)
.background {
AuthenticationStartScreenBackgroundImage()
}
.navigationBarHidden(context.viewState.classicAppMode == nil)
.toolbar { toolbar }
.alert(item: $context.alertInfo)
.sheet(isPresented: $context.showClassicAppBackupInstructions) {
AuthenticationClassicAppBackupInstructionsView(context: context)
}
.introspect(.window, on: .supportedVersions) { window in
context.send(viewAction: .updateWindow(window))
}
@@ -132,6 +139,17 @@ struct AuthenticationStartScreen: View {
let shortVersionString = ProcessInfo.isRunningTests ? "0.0.0" : InfoPlistReader.main.bundleShortVersionString
return Text(L10n.screenOnboardingAppVersion(shortVersionString))
}
@ToolbarContentBuilder
var toolbar: some ToolbarContent {
ToolbarItem(placement: .primaryAction) {
if case let .otherOptions(classicAppAccount) = context.viewState.classicAppMode {
ToolbarButton(role: .close) {
context.send(viewAction: .closeOtherOptions(classicAppAccount))
}
}
}
}
}
// MARK: - Previews
@@ -139,20 +157,32 @@ struct AuthenticationStartScreen: View {
struct AuthenticationStartScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = makeViewModel()
static let provisionedViewModel = makeViewModel(provisionedServerName: "example.com")
static let classicAppViewModel = makeViewModel(hasClassicAppAccount: true)
static var previews: some View {
AuthenticationStartScreen(context: viewModel.context)
.previewDisplayName("Default")
AuthenticationStartScreen(context: provisionedViewModel.context)
.previewDisplayName("Provisioned")
ElementNavigationStack {
AuthenticationStartScreen(context: classicAppViewModel.context)
}
.previewDisplayName("Classic App")
}
static func makeViewModel(provisionedServerName: String? = nil) -> AuthenticationStartScreenViewModel {
AuthenticationStartScreenViewModel(authenticationService: AuthenticationService.mock,
provisioningParameters: provisionedServerName.map { .init(accountProvider: $0, loginHint: nil) },
isBugReportServiceEnabled: true,
appSettings: ServiceLocator.shared.settings,
mediaProvider: MediaProviderMock(configuration: .init()),
userIndicatorController: UserIndicatorControllerMock())
static func makeViewModel(provisionedServerName: String? = nil, hasClassicAppAccount: Bool = false) -> AuthenticationStartScreenViewModel {
let classicAppAccount = ClassicAppAccount.mockDan
classicAppAccount.state.isServerSupported = true
classicAppAccount.state.availableSecrets = .complete
let classicAppManager: ClassicAppManagerMock? = hasClassicAppAccount ? .init(.init(accounts: [classicAppAccount])) : nil
return AuthenticationStartScreenViewModel(authenticationService: AuthenticationService.mock(classicAppManager: classicAppManager),
provisioningParameters: provisionedServerName.map { .init(accountProvider: $0, loginHint: nil) },
isBugReportServiceEnabled: true,
appMediator: AppMediatorMock(),
appSettings: ServiceLocator.shared.settings,
mediaProvider: MediaProviderMock(configuration: .init()),
userIndicatorController: UserIndicatorControllerMock())
}
}