// // Copyright 2025 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 AuthenticationClassicAppAccountView: View { @Bindable var context: AuthenticationStartScreenViewModel.Context let classicAppAccount: ClassicAppAccount var isLoadingAccount: Bool { classicAppAccount.state.isServerSupported == nil || classicAppAccount.state.availableSecrets == nil } var body: some View { FullscreenDialog(topPadding: 25, background: .gradient) { VStack(spacing: 38) { header .padding(.bottom, 20) profile buttons } } bottomContent: { // Buttons are intentionally shown inline on this screen. } .navigationBarTitleDisplayMode(.inline) .alert(item: $context.alertInfo) .sheet(isPresented: $context.showClassicAppBackupInstructions) { AuthenticationClassicAppBackupInstructionsView(classicAppAccount: classicAppAccount) { context.send(viewAction: .openClassicApp) } } .introspect(.window, on: .supportedVersions) { window in context.send(viewAction: .updateWindow(window)) } } var header: some View { VStack(spacing: 8) { AuthenticationStartLogo(size: 54, hideBrandChrome: false, isOnGradient: false) Text(L10n.screenOnboardingWelcomeTitle) .font(.compound.headingMDBold) .foregroundStyle(.compound.textPrimary) .multilineTextAlignment(.center) } } var profile: some View { VStack(spacing: 16) { LoadableAvatarImage(url: classicAppAccount.avatarURL, name: classicAppAccount.displayName, contentID: classicAppAccount.userID, avatarSize: .user(on: .classicAppAccount), mediaProvider: context.mediaProvider) VStack(spacing: 0) { Text(L10n.screenOnboardingWelcomeBack) .font(.compound.bodyMD) .foregroundStyle(.compound.textSecondary) .multilineTextAlignment(.center) Text(classicAppAccount.displayableName) .font(.compound.headingLGBold) .foregroundStyle(.compound.textPrimary) .multilineTextAlignment(.center) } Text(classicAppAccount.userID) .font(.compound.bodyLGSemibold) .foregroundStyle(.compound.textPrimary) .multilineTextAlignment(.center) } } var buttons: some View { VStack(spacing: 16) { if isLoadingAccount { Button { context.send(viewAction: .continueWithClassic(classicAppAccount)) } label: { Label { Text(L10n.screenOnboardingCheckingAccount) } icon: { ProgressView() .tint(.compound.iconOnSolidPrimary) } } .buttonStyle(.compound(.primary)) .disabled(true) } else { Button(L10n.actionContinue) { context.send(viewAction: .continueWithClassic(classicAppAccount)) } .buttonStyle(.compound(.primary)) Button(L10n.commonOtherOptions) { context.send(viewAction: .otherOptions(classicAppAccount)) } .buttonStyle(.compound(.secondary)) } } } } private extension ClassicAppAccount { var displayableName: String { if let displayName, !displayName.isEmpty { displayName } else if let localPart = userID.dropFirst().split(separator: ":").first, !localPart.isEmpty { String(localPart) } else { userID } } } // MARK: - Previews struct AuthenticationClassicAppAccountView_Previews: PreviewProvider { // Not Testable – snapshots generated by main screen. static let viewModel = makeViewModel() static let classicAppAccount = { let account = ClassicAppAccount.mockDan account.state.isServerSupported = true account.state.availableSecrets = .complete return account }() static var previews: some View { ElementNavigationStack { AuthenticationClassicAppAccountView(context: viewModel.context, classicAppAccount: classicAppAccount) } .previewDisplayName("Ready") ElementNavigationStack { AuthenticationClassicAppAccountView(context: viewModel.context, classicAppAccount: .mockDan) } .previewDisplayName("Loading") } static func makeViewModel() -> AuthenticationStartScreenViewModel { AuthenticationStartScreenViewModel(authenticationService: AuthenticationService.mock, provisioningParameters: nil, isBugReportServiceEnabled: false, appMediator: AppMediatorMock(), appSettings: ServiceLocator.shared.settings, mediaProvider: MediaProviderMock(configuration: .init()), userIndicatorController: UserIndicatorControllerMock()) } }