diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index a192bc1f5..2b77db540 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -54,6 +54,7 @@ final class AppSettings { case markAsFavouriteEnabled case roomModerationEnabled case publicSearchEnabled + case qrCodeLoginEnabled } private static var suiteName: String = InfoPlistReader.main.appGroupIdentifier @@ -287,6 +288,9 @@ final class AppSettings { @UserPreference(key: UserDefaultsKeys.publicSearchEnabled, defaultValue: false, storageType: .volatile) var publicSearchEnabled + @UserPreference(key: UserDefaultsKeys.qrCodeLoginEnabled, defaultValue: false, storageType: .userDefaults(store)) + var qrCodeLoginEnabled + #endif // MARK: - Shared diff --git a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift index dd38ba5b7..84b70a135 100644 --- a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift @@ -80,15 +80,18 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { // MARK: - Private private func showStartScreen() { - let coordinator = AuthenticationStartScreenCoordinator() + let coordinator = AuthenticationStartScreenCoordinator(parameters: .init(appSettings: appSettings)) coordinator.actions .sink { [weak self] action in guard let self else { return } switch action { - case .login: + case .loginManually: Task { await self.startAuthentication() } + case .loginWithQR: + // TODO: Implement QR code login navigation + break case .reportProblem: showReportProblemScreen() } diff --git a/ElementX/Sources/Other/AccessibilityIdentifiers.swift b/ElementX/Sources/Other/AccessibilityIdentifiers.swift index c5176a429..a00920512 100644 --- a/ElementX/Sources/Other/AccessibilityIdentifiers.swift +++ b/ElementX/Sources/Other/AccessibilityIdentifiers.swift @@ -125,6 +125,7 @@ enum A11yIdentifiers { struct AuthenticationStartScreen { let signIn = "authentication_start-sign_in" + let signInWithQr = "authentication_start-sign_in_with_qr" let hidden = "authentication_start-hidden" } diff --git a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenCoordinator.swift b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenCoordinator.swift index 2e452946e..ee11ed191 100644 --- a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenCoordinator.swift +++ b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenCoordinator.swift @@ -17,6 +17,10 @@ import Combine import SwiftUI +struct AuthenticationStartScreenCoordinatorParameters { + let appSettings: AppSettings +} + final class AuthenticationStartScreenCoordinator: CoordinatorProtocol { private var viewModel: AuthenticationStartScreenViewModelProtocol private let actionsSubject: PassthroughSubject = .init() @@ -26,8 +30,8 @@ final class AuthenticationStartScreenCoordinator: CoordinatorProtocol { actionsSubject.eraseToAnyPublisher() } - init() { - viewModel = AuthenticationStartScreenViewModel() + init(parameters: AuthenticationStartScreenCoordinatorParameters) { + viewModel = AuthenticationStartScreenViewModel(appSettings: parameters.appSettings) } // MARK: - Public @@ -38,8 +42,10 @@ final class AuthenticationStartScreenCoordinator: CoordinatorProtocol { guard let self else { return } switch action { - case .login: - actionsSubject.send(.login) + case .loginManually: + actionsSubject.send(.loginManually) + case .loginWithQR: + actionsSubject.send(.loginWithQR) case .reportProblem: actionsSubject.send(.reportProblem) } diff --git a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenModels.swift b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenModels.swift index 63790eaf4..17fa13a10 100644 --- a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenModels.swift +++ b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenModels.swift @@ -19,18 +19,23 @@ import SwiftUI // MARK: - Coordinator enum AuthenticationStartScreenCoordinatorAction { - case login + case loginManually + case loginWithQR case reportProblem } enum AuthenticationStartScreenViewModelAction { - case login + case loginManually + case loginWithQR case reportProblem } -struct AuthenticationStartScreenViewState: BindableState { } +struct AuthenticationStartScreenViewState: BindableState { + var isQRCodeLoginEnabled = false +} enum AuthenticationStartScreenViewAction { - case login + case loginManually + case loginWithQR case reportProblem } diff --git a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenViewModel.swift b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenViewModel.swift index b9ef0adb9..11653b4cf 100644 --- a/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenViewModel.swift +++ b/ElementX/Sources/Screens/AuthenticationStartScreen/AuthenticationStartScreenViewModel.swift @@ -20,20 +20,27 @@ import SwiftUI typealias AuthenticationStartScreenViewModelType = StateStoreViewModel class AuthenticationStartScreenViewModel: AuthenticationStartScreenViewModelType, AuthenticationStartScreenViewModelProtocol { + private let appSettings: AppSettings private var actionsSubject: PassthroughSubject = .init() var actions: AnyPublisher { actionsSubject.eraseToAnyPublisher() } - init() { + init(appSettings: AppSettings) { + self.appSettings = appSettings super.init(initialViewState: AuthenticationStartScreenViewState()) + appSettings.$qrCodeLoginEnabled + .weakAssign(to: \.state.isQRCodeLoginEnabled, on: self) + .store(in: &cancellables) } override func process(viewAction: AuthenticationStartScreenViewAction) { switch viewAction { - case .login: - actionsSubject.send(.login) + case .loginManually: + actionsSubject.send(.loginManually) + case .loginWithQR: + actionsSubject.send(.loginWithQR) case .reportProblem: actionsSubject.send(.reportProblem) } diff --git a/ElementX/Sources/Screens/AuthenticationStartScreen/View/AuthenticationStartScreen.swift b/ElementX/Sources/Screens/AuthenticationStartScreen/View/AuthenticationStartScreen.swift index ae3c3141c..44b731597 100644 --- a/ElementX/Sources/Screens/AuthenticationStartScreen/View/AuthenticationStartScreen.swift +++ b/ElementX/Sources/Screens/AuthenticationStartScreen/View/AuthenticationStartScreen.swift @@ -94,9 +94,17 @@ struct AuthenticationStartScreen: View { /// The main action buttons. var buttons: some View { - VStack(spacing: 12) { - Button { context.send(viewAction: .login) } label: { - Text(L10n.actionContinue) + VStack(spacing: 16) { + if context.viewState.isQRCodeLoginEnabled { + Button { context.send(viewAction: .loginManually) } label: { + Label(L10n.screenOnboardingSignInWithQrCode, icon: \.qrCode) + } + .buttonStyle(.compound(.primary)) + .accessibilityIdentifier(A11yIdentifiers.authenticationStartScreen.signInWithQr) + } + + Button { context.send(viewAction: .loginManually) } label: { + Text(context.viewState.isQRCodeLoginEnabled ? L10n.screenOnboardingSignInManually : L10n.actionContinue) } .buttonStyle(.compound(.primary)) .accessibilityIdentifier(A11yIdentifiers.authenticationStartScreen.signIn) @@ -109,7 +117,10 @@ struct AuthenticationStartScreen: View { // MARK: - Previews struct AuthenticationStartScreen_Previews: PreviewProvider, TestablePreview { - static let viewModel = AuthenticationStartScreenViewModel() + static let viewModel = { + ServiceLocator.shared.settings.qrCodeLoginEnabled = true + return AuthenticationStartScreenViewModel(appSettings: ServiceLocator.shared.settings) + }() static var previews: some View { AuthenticationStartScreen(context: viewModel.context) diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift index fee83e126..efedb4625 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 roomModerationEnabled: Bool { get set } var elementCallBaseURL: URL { get set } var publicSearchEnabled: Bool { get set } + var qrCodeLoginEnabled: 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 3a65533de..6046274da 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -53,6 +53,12 @@ struct DeveloperOptionsScreen: View { } } + Section("QR Code") { + Toggle(isOn: $context.qrCodeLoginEnabled) { + Text("QR code login") + } + } + Section("Element Call") { TextField(context.elementCallBaseURL.absoluteString, text: $elementCallBaseURLString) .submitLabel(.done) diff --git a/PreviewTests/__Snapshots__/PreviewTests/test_authenticationStartScreen-iPad-en-GB.1.png b/PreviewTests/__Snapshots__/PreviewTests/test_authenticationStartScreen-iPad-en-GB.1.png index 67f5e7c31..94e96b736 100644 --- a/PreviewTests/__Snapshots__/PreviewTests/test_authenticationStartScreen-iPad-en-GB.1.png +++ b/PreviewTests/__Snapshots__/PreviewTests/test_authenticationStartScreen-iPad-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6e28c5cbf66f7166b6cce05acdef3da61c396aab82a2648b36cc1995c4538c52 -size 1320362 +oid sha256:5db96b5725e55787e772dda0698e7d2d9c9c33b33e56a3746905582a4fe7c567 +size 1299604 diff --git a/PreviewTests/__Snapshots__/PreviewTests/test_authenticationStartScreen-iPad-pseudo.1.png b/PreviewTests/__Snapshots__/PreviewTests/test_authenticationStartScreen-iPad-pseudo.1.png index 8bd8ad753..0a731633a 100644 --- a/PreviewTests/__Snapshots__/PreviewTests/test_authenticationStartScreen-iPad-pseudo.1.png +++ b/PreviewTests/__Snapshots__/PreviewTests/test_authenticationStartScreen-iPad-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ca6c56a318aac4e14429770b14e1a068c6028dbbdae614065ce756569a2c513 -size 1373995 +oid sha256:ac5c752f55f6a5102ea8cb964aaad872e66636c98e84ba921a04c19f54fbfe27 +size 1360762 diff --git a/PreviewTests/__Snapshots__/PreviewTests/test_authenticationStartScreen-iPhone-15-en-GB.1.png b/PreviewTests/__Snapshots__/PreviewTests/test_authenticationStartScreen-iPhone-15-en-GB.1.png index 741ce9d2a..33e1abaf8 100644 --- a/PreviewTests/__Snapshots__/PreviewTests/test_authenticationStartScreen-iPhone-15-en-GB.1.png +++ b/PreviewTests/__Snapshots__/PreviewTests/test_authenticationStartScreen-iPhone-15-en-GB.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:75c51f7fd3ca983ef9906c7208238f00b053a68f7b5f81d9eea7623eecaac1a4 -size 549598 +oid sha256:71a18a8153f97586c0c8ceb6d02bbade1522a1b82509dc47106617a28f829bcf +size 551048 diff --git a/PreviewTests/__Snapshots__/PreviewTests/test_authenticationStartScreen-iPhone-15-pseudo.1.png b/PreviewTests/__Snapshots__/PreviewTests/test_authenticationStartScreen-iPhone-15-pseudo.1.png index 5b40790aa..e5471219b 100644 --- a/PreviewTests/__Snapshots__/PreviewTests/test_authenticationStartScreen-iPhone-15-pseudo.1.png +++ b/PreviewTests/__Snapshots__/PreviewTests/test_authenticationStartScreen-iPhone-15-pseudo.1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e1e82e135acc71f6cfd30b3dd9274e5aa83226f120c938fab87692581a7a36f -size 598130 +oid sha256:ab7ba10a2568b1b39f8f1fd27bdd1d1c4f66019c5c182b7bbcdbae65119c9f2d +size 601043 diff --git a/changelog.d/2621.wip b/changelog.d/2621.wip new file mode 100644 index 000000000..4bdda4ccd --- /dev/null +++ b/changelog.d/2621.wip @@ -0,0 +1 @@ +Added an FF for QR code login, and a CTA in the auth screen, to start the flow. \ No newline at end of file