Add a service and flow coordinator for the LinkNewDevice feature. (#4859)

* Add a LinkNewDeviceService that exposes the SDK's grant QR code login methods.

* Add a flow coordinator for linking a new device.

Changes the presentation too.
This commit is contained in:
Doug
2025-12-15 14:44:26 +00:00
committed by GitHub
parent 27ca265991
commit 93dfc84a57
38 changed files with 536 additions and 73 deletions
@@ -0,0 +1,64 @@
//
// 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 Combine
import Foundation
enum LinkNewDeviceFlowCoordinatorAction {
case requestOIDCAuthorisation(URL)
case dismiss
}
class LinkNewDeviceFlowCoordinator: FlowCoordinatorProtocol {
private let navigationStackCoordinator: NavigationStackCoordinator
private let flowParameters: CommonFlowParameters
private var cancellables = Set<AnyCancellable>()
private let actionsSubject: PassthroughSubject<LinkNewDeviceFlowCoordinatorAction, Never> = .init()
var actionsPublisher: AnyPublisher<LinkNewDeviceFlowCoordinatorAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
init(navigationStackCoordinator: NavigationStackCoordinator,
flowParameters: CommonFlowParameters) {
self.navigationStackCoordinator = navigationStackCoordinator
self.flowParameters = flowParameters
}
func start(animated: Bool) {
presentLinkNewDeviceScreen()
}
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
fatalError()
}
func clearRoute(animated: Bool) {
fatalError()
}
private func presentLinkNewDeviceScreen() {
let coordinator = LinkNewDeviceScreenCoordinator(parameters: .init(clientProxy: flowParameters.userSession.clientProxy))
coordinator.actionsPublisher
.sink { [weak self] action in
guard let self else { return }
switch action {
case .linkMobileDevice(let progressPublisher):
break
case .linkDesktopComputer:
break
case .dismiss:
actionsSubject.send(.dismiss)
}
}
.store(in: &cancellables)
navigationStackCoordinator.setRootCoordinator(coordinator)
}
}
@@ -28,6 +28,8 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
private var bugReportFlowCoordinator: BugReportFlowCoordinator?
// periphery:ignore - retaining purpose
private var encryptionSettingsFlowCoordinator: EncryptionSettingsFlowCoordinator?
// periphery:ignore - retaining purpose
private var linkNewDeviceFlowCoordinator: LinkNewDeviceFlowCoordinator?
private var cancellables = Set<AnyCancellable>()
@@ -84,7 +86,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
case .userDetails:
presentUserDetailsEditScreen()
case .linkNewDevice:
presentLinkNewDeviceScreen()
startLinkNewDeviceFlow()
case let .manageAccount(url):
presentAccountManagementURL(url)
case .analytics:
@@ -169,6 +171,31 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
navigationStackCoordinator.push(coordinator)
}
private func startLinkNewDeviceFlow() {
let stackCoordinator = NavigationStackCoordinator()
let flowCoordinator = LinkNewDeviceFlowCoordinator(navigationStackCoordinator: stackCoordinator,
flowParameters: flowParameters)
flowCoordinator.actionsPublisher
.sink { [weak self] action in
guard let self else { return }
switch action {
case .dismiss:
navigationStackCoordinator.setSheetCoordinator(nil)
case .requestOIDCAuthorisation(let url):
presentAccountManagementURL(url)
}
}
.store(in: &cancellables)
linkNewDeviceFlowCoordinator = flowCoordinator
flowCoordinator.start()
navigationStackCoordinator.setSheetCoordinator(stackCoordinator) { [weak self] in
self?.linkNewDeviceFlowCoordinator = nil
}
}
private func presentAnalyticsScreen() {
let coordinator = AnalyticsSettingsScreenCoordinator(parameters: .init(appSettings: flowParameters.appSettings,
analytics: flowParameters.analytics))
@@ -259,9 +286,9 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
navigationStackCoordinator.push(coordinator)
}
// MARK: OIDC Account Management
private var accountSettingsPresenter: OIDCAccountSettingsPresenter?
private func presentAccountManagementURL(_ url: URL) {
// Note to anyone in the future if you come back here to make this open in Safari instead of a WAS.
@@ -271,27 +298,4 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
appSettings: flowParameters.appSettings)
accountSettingsPresenter?.start()
}
// MARK: - Link New Device
private func presentLinkNewDeviceScreen() {
let parameters = LinkNewDeviceScreenCoordinatorParameters(clientProxy: flowParameters.userSession.clientProxy)
let coordinator = LinkNewDeviceScreenCoordinator(parameters: parameters)
coordinator.actionsPublisher
.sink { [weak self] action in
guard let self else { return }
switch action {
case .linkMobileDevice:
break
case .linkDesktopComputer:
break
case .dismiss:
navigationStackCoordinator.pop()
}
}
.store(in: &cancellables)
navigationStackCoordinator.push(coordinator)
}
}