QR Login additional flags (#6825)

This commit is contained in:
ismailgulek 2022-10-07 17:24:29 +03:00 committed by GitHub
parent 1ee8c9ca19
commit 359bfc0445
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 125 additions and 27 deletions

View file

@ -422,5 +422,11 @@ final class BuildSettings: NSObject {
static let newAppLayoutEnabled = true
// MARK: - QR Login
static let enableQRLogin = false
/// Flag indicating whether the QR login enabled from login screen
static let qrLoginEnabledFromNotAuthenticated = false
/// Flag indicating whether the QR login enabled from Device Manager screen
static let qrLoginEnabledFromAuthenticated = false
/// Flag indicating whether displaying QRs enabled for the QR login screens
static let qrLoginEnableDisplayingQRs = false
}

View file

@ -54,12 +54,23 @@ class QRLoginService: NSObject, QRLoginServiceProtocol {
let callbacks = PassthroughSubject<QRLoginServiceCallback, Never>()
func isServiceAvailable() async throws -> Bool {
guard BuildSettings.enableQRLogin else {
return false
switch mode {
case .authenticated:
guard BuildSettings.qrLoginEnabledFromAuthenticated else {
return false
}
case .notAuthenticated:
guard BuildSettings.qrLoginEnabledFromNotAuthenticated else {
return false
}
}
return try await client.supportedMatrixVersions().supportsQRLogin
}
func canDisplayQR() -> Bool {
BuildSettings.qrLoginEnableDisplayingQRs
}
func generateQRCode() async throws -> QRLoginCode {
let transport = QRLoginRendezvousTransportDetails(type: "http.v1",
uri: "")

View file

@ -19,10 +19,14 @@ import Foundation
import SwiftUI
class MockQRLoginService: QRLoginServiceProtocol {
private let mockCanDisplayQR: Bool
init(withState state: QRLoginServiceState = .initial,
mode: QRLoginServiceMode = .notAuthenticated) {
mode: QRLoginServiceMode = .notAuthenticated,
canDisplayQR: Bool = true) {
self.state = state
self.mode = mode
self.mockCanDisplayQR = canDisplayQR
}
// MARK: - QRLoginServiceProtocol
@ -43,6 +47,10 @@ class MockQRLoginService: QRLoginServiceProtocol {
true
}
func canDisplayQR() -> Bool {
mockCanDisplayQR
}
func generateQRCode() async throws -> QRLoginCode {
let transport = QRLoginRendezvousTransportDetails(type: "http.v1",
uri: "https://matrix.org")

View file

@ -82,6 +82,7 @@ protocol QRLoginServiceProtocol {
var state: QRLoginServiceState { get }
var callbacks: PassthroughSubject<QRLoginServiceCallback, Never> { get }
func isServiceAvailable() async throws -> Bool
func canDisplayQR() -> Bool
func generateQRCode() async throws -> QRLoginCode
// MARK: QR Scanner

View file

@ -43,6 +43,7 @@ enum AuthenticationQRLoginScanViewModelResult: Equatable {
// MARK: View
struct AuthenticationQRLoginScanViewState: BindableState {
var canShowDisplayQRButton: Bool
var serviceState: QRLoginServiceState
var scannerView: AnyView?
}

View file

@ -34,7 +34,8 @@ class AuthenticationQRLoginScanViewModel: AuthenticationQRLoginScanViewModelType
init(qrLoginService: QRLoginServiceProtocol) {
self.qrLoginService = qrLoginService
super.init(initialViewState: AuthenticationQRLoginScanViewState(serviceState: .initial))
super.init(initialViewState: .init(canShowDisplayQRButton: qrLoginService.canDisplayQR(),
serviceState: .initial))
qrLoginService.callbacks.sink { callback in
switch callback {

View file

@ -26,6 +26,8 @@ enum MockAuthenticationQRLoginScanScreenState: MockScreenState, CaseIterable {
case scanning
case noCameraAvailable
case noCameraAccess
case noCameraAvailableNoDisplayQR
case noCameraAccessNoDisplayQR
/// The associated screen
var screenType: Any.Type {
@ -35,21 +37,27 @@ enum MockAuthenticationQRLoginScanScreenState: MockScreenState, CaseIterable {
/// A list of screen state definitions
static var allCases: [MockAuthenticationQRLoginScanScreenState] {
// Each of the presence statuses
[.scanning, .noCameraAvailable, .noCameraAccess]
[.scanning, .noCameraAvailable, .noCameraAccess, .noCameraAvailableNoDisplayQR, .noCameraAccessNoDisplayQR]
}
/// Generate the view struct for the screen state.
var screenView: ([Any], AnyView) {
let viewModel: AuthenticationQRLoginScanViewModel
let service: QRLoginServiceProtocol
switch self {
case .scanning:
viewModel = .init(qrLoginService: MockQRLoginService(withState: .scanningQR))
service = MockQRLoginService(withState: .scanningQR)
case .noCameraAvailable:
viewModel = .init(qrLoginService: MockQRLoginService(withState: .failed(error: .noCameraAvailable)))
service = MockQRLoginService(withState: .failed(error: .noCameraAvailable))
case .noCameraAccess:
viewModel = .init(qrLoginService: MockQRLoginService(withState: .failed(error: .noCameraAccess)))
service = MockQRLoginService(withState: .failed(error: .noCameraAccess))
case .noCameraAvailableNoDisplayQR:
service = MockQRLoginService(withState: .failed(error: .noCameraAvailable), canDisplayQR: false)
case .noCameraAccessNoDisplayQR:
service = MockQRLoginService(withState: .failed(error: .noCameraAccess), canDisplayQR: false)
}
let viewModel = AuthenticationQRLoginScanViewModel(qrLoginService: service)
// can simulate service and viewModel actions here if needs be.

View file

@ -50,4 +50,28 @@ class AuthenticationQRLoginScanUITests: MockScreenTestCase {
XCTAssertTrue(displayQRButton.exists)
XCTAssertTrue(displayQRButton.isEnabled)
}
func testNoCameraAvailableNoDisplayQR() {
app.goToScreenWithIdentifier(MockAuthenticationQRLoginScanScreenState.noCameraAvailableNoDisplayQR.title)
XCTAssertTrue(app.staticTexts["titleLabel"].exists)
XCTAssertTrue(app.staticTexts["subtitleLabel"].exists)
let displayQRButton = app.buttons["displayQRButton"]
XCTAssertFalse(displayQRButton.exists)
}
func testNoCameraAccessNoDisplayQR() {
app.goToScreenWithIdentifier(MockAuthenticationQRLoginScanScreenState.noCameraAccessNoDisplayQR.title)
XCTAssertTrue(app.staticTexts["titleLabel"].exists)
XCTAssertTrue(app.staticTexts["subtitleLabel"].exists)
let openSettingsButton = app.buttons["openSettingsButton"]
XCTAssertTrue(openSettingsButton.exists)
XCTAssertTrue(openSettingsButton.isEnabled)
let displayQRButton = app.buttons["displayQRButton"]
XCTAssertFalse(displayQRButton.exists)
}
}

View file

@ -27,6 +27,10 @@ class AuthenticationQRLoginScanViewModelTests: XCTestCase {
context = viewModel.context
}
func testDisplayQRButtonVisibility() {
XCTAssertTrue(viewModel.context.viewState.canShowDisplayQRButton)
}
func testGoToSettings() {
var result: AuthenticationQRLoginScanViewModelResult?

View file

@ -167,13 +167,15 @@ struct AuthenticationQRLoginScanScreen: View {
.accessibilityIdentifier("openSettingsButton")
}
LabelledDivider(label: VectorL10n.authenticationQrLoginStartNeedAlternative)
if context.viewState.canShowDisplayQRButton {
LabelledDivider(label: VectorL10n.authenticationQrLoginStartNeedAlternative)
Button(action: displayQR) {
Text(VectorL10n.authenticationQrLoginStartDisplayQr)
Button(action: displayQR) {
Text(VectorL10n.authenticationQrLoginStartDisplayQr)
}
.buttonStyle(SecondaryActionButtonStyle(font: theme.fonts.bodySB))
.accessibilityIdentifier("displayQRButton")
}
.buttonStyle(SecondaryActionButtonStyle(font: theme.fonts.bodySB))
.accessibilityIdentifier("displayQRButton")
}
}

View file

@ -27,7 +27,9 @@ enum AuthenticationQRLoginStartViewModelResult {
// MARK: View
struct AuthenticationQRLoginStartViewState: BindableState { }
struct AuthenticationQRLoginStartViewState: BindableState {
var canShowDisplayQRButton: Bool
}
enum AuthenticationQRLoginStartViewAction {
case scanQR

View file

@ -33,7 +33,7 @@ class AuthenticationQRLoginStartViewModel: AuthenticationQRLoginStartViewModelTy
init(qrLoginService: QRLoginServiceProtocol) {
self.qrLoginService = qrLoginService
super.init(initialViewState: AuthenticationQRLoginStartViewState())
super.init(initialViewState: AuthenticationQRLoginStartViewState(canShowDisplayQRButton: qrLoginService.canDisplayQR()))
}
// MARK: - Public

View file

@ -23,7 +23,8 @@ enum MockAuthenticationQRLoginStartScreenState: MockScreenState, CaseIterable {
// A case for each state you want to represent
// with specific, minimal associated data that will allow you
// mock that screen.
case `default`
case displayQREnabled
case displayQRDisabled
/// The associated screen
var screenType: Any.Type {
@ -33,12 +34,21 @@ enum MockAuthenticationQRLoginStartScreenState: MockScreenState, CaseIterable {
/// A list of screen state definitions
static var allCases: [MockAuthenticationQRLoginStartScreenState] {
// Each of the presence statuses
[.default]
[.displayQREnabled, .displayQRDisabled]
}
/// Generate the view struct for the screen state.
var screenView: ([Any], AnyView) {
let viewModel = AuthenticationQRLoginStartViewModel(qrLoginService: MockQRLoginService())
let service: QRLoginServiceProtocol
switch self {
case .displayQREnabled:
service = MockQRLoginService(canDisplayQR: true)
case .displayQRDisabled:
service = MockQRLoginService(canDisplayQR: false)
}
let viewModel = AuthenticationQRLoginStartViewModel(qrLoginService: service)
// can simulate service and viewModel actions here if needs be.

View file

@ -18,8 +18,8 @@ import RiotSwiftUI
import XCTest
class AuthenticationQRLoginStartUITests: MockScreenTestCase {
func testDefault() {
app.goToScreenWithIdentifier(MockAuthenticationQRLoginStartScreenState.default.title)
func testDisplayQREnabled() {
app.goToScreenWithIdentifier(MockAuthenticationQRLoginStartScreenState.displayQREnabled.title)
XCTAssertTrue(app.staticTexts["titleLabel"].exists)
XCTAssertTrue(app.staticTexts["subtitleLabel"].exists)
@ -32,4 +32,18 @@ class AuthenticationQRLoginStartUITests: MockScreenTestCase {
XCTAssertTrue(displayQRButton.exists)
XCTAssertTrue(displayQRButton.isEnabled)
}
func testDisplayQRDisabled() {
app.goToScreenWithIdentifier(MockAuthenticationQRLoginStartScreenState.displayQRDisabled.title)
XCTAssertTrue(app.staticTexts["titleLabel"].exists)
XCTAssertTrue(app.staticTexts["subtitleLabel"].exists)
let scanQRButton = app.buttons["scanQRButton"]
XCTAssertTrue(scanQRButton.exists)
XCTAssertTrue(scanQRButton.isEnabled)
let displayQRButton = app.buttons["displayQRButton"]
XCTAssertFalse(displayQRButton.exists)
}
}

View file

@ -27,6 +27,10 @@ class AuthenticationQRLoginStartViewModelTests: XCTestCase {
context = viewModel.context
}
func testDisplayQRButtonVisibility() {
XCTAssertTrue(viewModel.context.viewState.canShowDisplayQRButton)
}
func testScanQR() {
var result: AuthenticationQRLoginStartViewModelResult?

View file

@ -88,13 +88,15 @@ struct AuthenticationQRLoginStartScreen: View {
.padding(.bottom, 8)
.accessibilityIdentifier("scanQRButton")
LabelledDivider(label: VectorL10n.authenticationQrLoginStartNeedAlternative)
if context.viewState.canShowDisplayQRButton {
LabelledDivider(label: VectorL10n.authenticationQrLoginStartNeedAlternative)
Button(action: displayQR) {
Text(VectorL10n.authenticationQrLoginStartDisplayQr)
Button(action: displayQR) {
Text(VectorL10n.authenticationQrLoginStartDisplayQr)
}
.buttonStyle(SecondaryActionButtonStyle(font: theme.fonts.bodySB))
.accessibilityIdentifier("displayQRButton")
}
.buttonStyle(SecondaryActionButtonStyle(font: theme.fonts.bodySB))
.accessibilityIdentifier("displayQRButton")
}
}