mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 21:33:26 +00:00
Add support for manually starting SaS verification flows and accepting remotely started ones
This commit is contained in:
committed by
Stefan Ceriu
parent
7888736a4d
commit
956e92a9ac
@@ -22,22 +22,19 @@ enum SessionVerificationViewModelAction {
|
||||
|
||||
struct SessionVerificationViewState: BindableState {
|
||||
var verificationState: SessionVerificationStateMachine.State = .initial
|
||||
|
||||
var title: String? {
|
||||
switch verificationState {
|
||||
case .showingChallenge:
|
||||
return ElementL10n.sessionVerificationScreenEmojisTitle
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var message: String {
|
||||
switch verificationState {
|
||||
case .initial:
|
||||
return ElementL10n.verificationOpenOtherToVerify
|
||||
case .requestingVerification:
|
||||
return ElementL10n.verificationRequestWaiting
|
||||
case .verificationRequestAccepted:
|
||||
return ElementL10n.verificationEmojiNotice
|
||||
case .startingSasVerification:
|
||||
return ElementL10n.verificationRequestWaiting
|
||||
case .sasVerificationStarted:
|
||||
return ElementL10n.verificationRequestWaiting
|
||||
case .acceptingChallenge:
|
||||
return ElementL10n.verificationRequestWaiting
|
||||
case .decliningChallenge:
|
||||
@@ -45,7 +42,7 @@ struct SessionVerificationViewState: BindableState {
|
||||
case .cancelling:
|
||||
return ElementL10n.verificationRequestWaiting
|
||||
case .showingChallenge:
|
||||
return ElementL10n.sessionVerificationScreenEmojisMessage
|
||||
return ElementL10n.verificationCodeNotice
|
||||
case .verified:
|
||||
return ElementL10n.verificationConclusionOkSelfNotice
|
||||
case .cancelled:
|
||||
@@ -55,7 +52,8 @@ struct SessionVerificationViewState: BindableState {
|
||||
}
|
||||
|
||||
enum SessionVerificationViewAction {
|
||||
case start
|
||||
case requestVerification
|
||||
case startSasVerification
|
||||
case restart
|
||||
case accept
|
||||
case decline
|
||||
|
||||
@@ -24,6 +24,12 @@ class SessionVerificationStateMachine {
|
||||
case initial
|
||||
/// Waiting for verification acceptance
|
||||
case requestingVerification
|
||||
/// Verification request accepted. Waiting for start
|
||||
case verificationRequestAccepted
|
||||
/// Waiting for SaS verification start
|
||||
case startingSasVerification
|
||||
/// A SaS verification flow has been started
|
||||
case sasVerificationStarted
|
||||
/// Verification accepted and emojis received
|
||||
case showingChallenge(emojis: [SessionVerificationEmoji])
|
||||
/// Emojis match locally
|
||||
@@ -42,6 +48,12 @@ class SessionVerificationStateMachine {
|
||||
enum Event: EventType {
|
||||
/// Request verification
|
||||
case requestVerification
|
||||
/// The current verification request has been accepted
|
||||
case didAcceptVerificationRequest
|
||||
/// Start a SaS verification flow
|
||||
case startSasVerification
|
||||
/// Started a SaS verification flow
|
||||
case didStartSasVerification
|
||||
/// Has received emojis
|
||||
case didReceiveChallenge(emojis: [SessionVerificationEmoji])
|
||||
/// Emojis match
|
||||
@@ -69,15 +81,19 @@ class SessionVerificationStateMachine {
|
||||
init() {
|
||||
stateMachine = StateMachine(state: .initial) { machine in
|
||||
machine.addRoutes(event: .requestVerification, transitions: [.initial => .requestingVerification])
|
||||
machine.addRoutes(event: .didAcceptVerificationRequest, transitions: [.requestingVerification => .verificationRequestAccepted])
|
||||
machine.addRoutes(event: .startSasVerification, transitions: [.verificationRequestAccepted => .startingSasVerification])
|
||||
machine.addRoutes(event: .didFail, transitions: [.requestingVerification => .initial])
|
||||
machine.addRoutes(event: .restart, transitions: [.cancelled => .initial])
|
||||
|
||||
// Transitions with associated values need to be handled through `addRouteMapping`
|
||||
machine.addRouteMapping { event, fromState, _ in
|
||||
switch (event, fromState) {
|
||||
case (.didReceiveChallenge(let emojis), .requestingVerification):
|
||||
case (.didStartSasVerification, _):
|
||||
return .sasVerificationStarted
|
||||
|
||||
case (.didReceiveChallenge(let emojis), .sasVerificationStarted):
|
||||
return .showingChallenge(emojis: emojis)
|
||||
|
||||
case (.acceptChallenge, .showingChallenge(let emojis)):
|
||||
return .acceptingChallenge(emojis: emojis)
|
||||
case (.didFail, .acceptingChallenge(let emojis)):
|
||||
@@ -100,6 +116,14 @@ class SessionVerificationStateMachine {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
addTransitionHandler { context in
|
||||
if let event = context.event {
|
||||
MXLog.info("Transitioning from `\(context.fromState)` to `\(context.toState)` with event `\(event)`")
|
||||
} else {
|
||||
MXLog.info("Transitioning from \(context.fromState)` to `\(context.toState)`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,12 @@ class SessionVerificationViewModel: SessionVerificationViewModelType, SessionVer
|
||||
guard let self else { return }
|
||||
|
||||
switch callback {
|
||||
case .acceptedVerificationRequest:
|
||||
self.stateMachine.processEvent(.didAcceptVerificationRequest)
|
||||
case .startedSasVerification:
|
||||
self.stateMachine.processEvent(.didStartSasVerification)
|
||||
case .receivedVerificationData(let emojis):
|
||||
guard self.stateMachine.state == .requestingVerification else {
|
||||
guard self.stateMachine.state == .sasVerificationStarted else {
|
||||
MXLog.warning("Callbacks: Ignoring receivedVerificationData due to invalid state.")
|
||||
return
|
||||
}
|
||||
@@ -61,8 +65,10 @@ class SessionVerificationViewModel: SessionVerificationViewModelType, SessionVer
|
||||
|
||||
override func process(viewAction: SessionVerificationViewAction) async {
|
||||
switch viewAction {
|
||||
case .start:
|
||||
case .requestVerification:
|
||||
stateMachine.processEvent(.requestVerification)
|
||||
case .startSasVerification:
|
||||
stateMachine.processEvent(.startSasVerification)
|
||||
case .restart:
|
||||
stateMachine.processEvent(.restart)
|
||||
case .close:
|
||||
@@ -92,6 +98,8 @@ class SessionVerificationViewModel: SessionVerificationViewModelType, SessionVer
|
||||
switch (context.fromState, context.event, context.toState) {
|
||||
case (.initial, .requestVerification, .requestingVerification):
|
||||
self.requestVerification()
|
||||
case (.verificationRequestAccepted, .startSasVerification, .startingSasVerification):
|
||||
self.startSasVerification()
|
||||
case (.showingChallenge, .acceptChallenge, .acceptingChallenge):
|
||||
self.acceptChallenge()
|
||||
case (.showingChallenge, .declineChallenge, .decliningChallenge):
|
||||
@@ -131,6 +139,18 @@ class SessionVerificationViewModel: SessionVerificationViewModelType, SessionVer
|
||||
}
|
||||
}
|
||||
|
||||
private func startSasVerification() {
|
||||
Task {
|
||||
switch await sessionVerificationControllerProxy.startSasVerification() {
|
||||
case .success:
|
||||
// Need to wait for the callback from the remote
|
||||
break
|
||||
case .failure:
|
||||
stateMachine.processEvent(.didFail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func acceptChallenge() {
|
||||
Task {
|
||||
switch await sessionVerificationControllerProxy.approveVerification() {
|
||||
|
||||
@@ -23,13 +23,6 @@ struct SessionVerificationScreen: View {
|
||||
NavigationView {
|
||||
ScrollView {
|
||||
VStack(spacing: 32.0) {
|
||||
if let title = context.viewState.title {
|
||||
Text(title)
|
||||
.font(.element.headlineBold)
|
||||
.foregroundColor(.element.systemPrimaryLabel)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
|
||||
Text(context.viewState.message)
|
||||
.font(.body)
|
||||
.multilineTextAlignment(.center)
|
||||
@@ -66,6 +59,15 @@ struct SessionVerificationScreen: View {
|
||||
case .requestingVerification:
|
||||
ProgressView()
|
||||
.accessibilityIdentifier("requestingVerificationProgressView")
|
||||
|
||||
case .verificationRequestAccepted:
|
||||
StateIcon(systemName: "lock.shield")
|
||||
case .startingSasVerification:
|
||||
ProgressView()
|
||||
.accessibilityIdentifier("startingSasVerification")
|
||||
case .sasVerificationStarted:
|
||||
ProgressView()
|
||||
.accessibilityIdentifier("startedSasVerification")
|
||||
case .cancelling:
|
||||
ProgressView()
|
||||
.accessibilityIdentifier("cancellingVerificationProgressView")
|
||||
@@ -98,10 +100,10 @@ struct SessionVerificationScreen: View {
|
||||
switch context.viewState.verificationState {
|
||||
case .initial:
|
||||
Button(ElementL10n.startVerification) {
|
||||
context.send(viewAction: .start)
|
||||
context.send(viewAction: .requestVerification)
|
||||
}
|
||||
.buttonStyle(.elementAction(.xLarge))
|
||||
.accessibilityIdentifier("startButton")
|
||||
.accessibilityIdentifier("requestVerificationButton")
|
||||
|
||||
case .cancelled:
|
||||
Button(ElementL10n.globalRetry) {
|
||||
@@ -109,16 +111,23 @@ struct SessionVerificationScreen: View {
|
||||
}
|
||||
.buttonStyle(.elementAction(.xLarge))
|
||||
.accessibilityIdentifier("restartButton")
|
||||
|
||||
case .verificationRequestAccepted:
|
||||
Button(ElementL10n.startVerification) {
|
||||
context.send(viewAction: .startSasVerification)
|
||||
}
|
||||
.buttonStyle(.elementAction(.xLarge))
|
||||
.accessibilityIdentifier("sasVerificationStartButton")
|
||||
|
||||
case .showingChallenge:
|
||||
VStack(spacing: 30) {
|
||||
Button { context.send(viewAction: .accept) } label: {
|
||||
Label(ElementL10n.actionMatch, systemImage: "checkmark")
|
||||
Label(ElementL10n.verificationSasMatch, systemImage: "checkmark")
|
||||
}
|
||||
.buttonStyle(.elementAction(.xLarge))
|
||||
.accessibilityLabel("challengeAcceptButton")
|
||||
|
||||
Button(ElementL10n.no) {
|
||||
Button(ElementL10n.verificationSasDoNotMatch) {
|
||||
context.send(viewAction: .decline)
|
||||
}
|
||||
.font(.element.bodyBold)
|
||||
|
||||
Reference in New Issue
Block a user