mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-18 23:48:29 +02:00
Login with QR UI components (#6790)
* Display QR button on login screen if HS supports * Create start screen * Add build flag * Connect start screen to the login * QR display screen * Move `LabelledDividerView` into separate file * Show display QR screen on button tap * Add swift concurreny to CameraAccessManager * Introduce `QRLoginServiceProtocol` * Use new service in screens * Introduce scan QR code screen * Remove hardcoded service availability * Remove unnecessary import * Add confirmation screen * Add loading screen * Fix ZXingObjc targets * Add failure screen * Add strings * Various UI tweaks, navigation according to the service state * Fix tests * Add string for invalid QR error * Add QR login service mode
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import AVFoundation
|
||||
import Combine
|
||||
import Foundation
|
||||
import MatrixSDK
|
||||
import SwiftUI
|
||||
import ZXingObjC
|
||||
|
||||
// MARK: - QRLoginService
|
||||
|
||||
class QRLoginService: NSObject, QRLoginServiceProtocol {
|
||||
private let client: AuthenticationRestClient
|
||||
private var isCameraReady = false
|
||||
private lazy var zxCapture = ZXCapture()
|
||||
|
||||
private let cameraAccessManager = CameraAccessManager()
|
||||
|
||||
init(client: AuthenticationRestClient,
|
||||
mode: QRLoginServiceMode,
|
||||
state: QRLoginServiceState = .initial) {
|
||||
self.client = client
|
||||
self.mode = mode
|
||||
self.state = state
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: QRLoginServiceProtocol
|
||||
|
||||
let mode: QRLoginServiceMode
|
||||
|
||||
var state: QRLoginServiceState {
|
||||
didSet {
|
||||
if state != oldValue {
|
||||
callbacks.send(.didUpdateState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let callbacks = PassthroughSubject<QRLoginServiceCallback, Never>()
|
||||
|
||||
func isServiceAvailable() async throws -> Bool {
|
||||
guard BuildSettings.enableQRLogin else {
|
||||
return false
|
||||
}
|
||||
return try await client.supportedMatrixVersions().supportsQRLogin
|
||||
}
|
||||
|
||||
func generateQRCode() async throws -> QRLoginCode {
|
||||
let transport = QRLoginRendezvousTransportDetails(type: "http.v1",
|
||||
uri: "")
|
||||
let rendezvous = QRLoginRendezvous(transport: transport,
|
||||
algorithm: "m.rendezvous.v1.curve25519-aes-sha256",
|
||||
key: "")
|
||||
return QRLoginCode(user: client.credentials.userId,
|
||||
initiator: .new,
|
||||
rendezvous: rendezvous)
|
||||
}
|
||||
|
||||
func scannerView() -> AnyView {
|
||||
let frame = UIScreen.main.bounds
|
||||
let view = UIView(frame: frame)
|
||||
zxCapture.layer.frame = frame
|
||||
view.layer.addSublayer(zxCapture.layer)
|
||||
return AnyView(ViewWrapper(view: view))
|
||||
}
|
||||
|
||||
func startScanning() {
|
||||
Task { @MainActor in
|
||||
if cameraAccessManager.isCameraAvailable {
|
||||
let granted = await cameraAccessManager.requestCameraAccessIfNeeded()
|
||||
if granted {
|
||||
state = .scanningQR
|
||||
zxCapture.delegate = self
|
||||
zxCapture.camera = zxCapture.back()
|
||||
zxCapture.start()
|
||||
} else {
|
||||
state = .failed(error: .noCameraAccess)
|
||||
}
|
||||
} else {
|
||||
state = .failed(error: .noCameraAvailable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stopScanning(destroy: Bool) {
|
||||
guard zxCapture.running else {
|
||||
return
|
||||
}
|
||||
|
||||
if destroy {
|
||||
zxCapture.hard_stop()
|
||||
} else {
|
||||
zxCapture.stop()
|
||||
}
|
||||
}
|
||||
|
||||
func processScannedQR(_ data: Data) {
|
||||
state = .connectingToDevice
|
||||
do {
|
||||
let code = try JSONDecoder().decode(QRLoginCode.self, from: data)
|
||||
MXLog.debug("[QRLoginService] processScannedQR: \(code)")
|
||||
// TODO: implement
|
||||
} catch {
|
||||
state = .failed(error: .invalidQR)
|
||||
}
|
||||
}
|
||||
|
||||
func confirmCode() {
|
||||
switch state {
|
||||
case .waitingForConfirmation(let code):
|
||||
// TODO: implement
|
||||
break
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func restart() {
|
||||
state = .initial
|
||||
}
|
||||
|
||||
func reset() {
|
||||
stopScanning(destroy: false)
|
||||
state = .initial
|
||||
}
|
||||
|
||||
deinit {
|
||||
stopScanning(destroy: true)
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
}
|
||||
|
||||
// MARK: - ZXCaptureDelegate
|
||||
|
||||
extension QRLoginService: ZXCaptureDelegate {
|
||||
func captureCameraIsReady(_ capture: ZXCapture!) {
|
||||
isCameraReady = true
|
||||
}
|
||||
|
||||
func captureResult(_ capture: ZXCapture!, result: ZXResult!) {
|
||||
guard isCameraReady,
|
||||
let result = result,
|
||||
result.barcodeFormat == kBarcodeFormatQRCode else {
|
||||
return
|
||||
}
|
||||
|
||||
stopScanning(destroy: false)
|
||||
|
||||
if let bytes = result.resultMetadata.object(forKey: kResultMetadataTypeByteSegments.rawValue) as? NSArray,
|
||||
let byteArray = bytes.firstObject as? ZXByteArray {
|
||||
let data = Data(bytes: UnsafeRawPointer(byteArray.array), count: Int(byteArray.length))
|
||||
|
||||
callbacks.send(.didScanQR(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ViewWrapper
|
||||
|
||||
private struct ViewWrapper: UIViewRepresentable {
|
||||
var view: UIView
|
||||
|
||||
func makeUIView(context: Context) -> some UIView {
|
||||
view
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: UIViewType, context: Context) { }
|
||||
}
|
||||
Reference in New Issue
Block a user