Add support for linking new devices in the QRCodeLoginScreen. (#4891)

* Adds the remaining parts for showing/scanning a QR code to link a new device.

* Refactor the QRCodeLoginService to work the same way as the LinkNewDeviceService.
This commit is contained in:
Doug
2026-01-07 12:18:39 +00:00
committed by GitHub
parent 9269f3057b
commit c1bb838517
62 changed files with 959 additions and 388 deletions
@@ -111,7 +111,58 @@ enum QRCodeLoginError: Error, Equatable {
// sourcery: AutoMockable
protocol QRCodeLoginServiceProtocol {
var qrLoginProgressPublisher: AnyPublisher<QrLoginProgress, Never> { get }
func loginWithQRCode(data: Data) async -> Result<UserSessionProtocol, AuthenticationServiceError>
typealias QRLoginProgressPublisher = CurrentValuePublisher<QRLoginProgress, AuthenticationServiceError>
func loginWithQRCode(data: Data) -> QRLoginProgressPublisher
}
enum QRLoginProgress {
case starting
case establishingSecureChannel(checkCode: UInt8, checkCodeString: String)
case waitingForToken(userCode: String)
case syncingSecrets
case signedIn(UserSessionProtocol)
init?(rustProgress: QrLoginProgress) {
switch rustProgress {
case .starting:
self = .starting
case .establishingSecureChannel(let checkCode, let checkCodeString):
self = .establishingSecureChannel(checkCode: checkCode, checkCodeString: checkCodeString)
case .waitingForToken(let userCode):
self = .waitingForToken(userCode: userCode)
case .syncingSecrets:
self = .syncingSecrets
case .done:
return nil // The SDK is done, but the app still needs to set up the UserSession.
}
}
}
extension QRLoginProgress: Equatable, CustomStringConvertible {
static func == (lhs: QRLoginProgress, rhs: QRLoginProgress) -> Bool {
switch (lhs, rhs) {
case (.starting, .starting):
true
case let (.establishingSecureChannel(lhsCheckCode, lhsCheckCodeString), .establishingSecureChannel(rhsCheckCode, rhsCheckCodeString)):
lhsCheckCode == rhsCheckCode && lhsCheckCodeString == rhsCheckCodeString
case let (.waitingForToken(lhsUserCode), .waitingForToken(rhsUserCode)):
lhsUserCode == rhsUserCode
case (.syncingSecrets, .syncingSecrets):
true
case (.signedIn, .signedIn):
true
default:
false
}
}
var description: String {
switch self {
case .starting: "starting"
case .establishingSecureChannel: "establishingSecureChannel"
case .waitingForToken: "waitingForToken"
case .syncingSecrets: "syncingSecrets"
case .signedIn: "signedIn"
}
}
}