Skip to content

Commit

Permalink
Add support for initiating and responding to user verification reques…
Browse files Browse the repository at this point in the history
…ts (#3759)
  • Loading branch information
stefanceriu authored Feb 10, 2025
1 parent 22d0fae commit 8680d84
Show file tree
Hide file tree
Showing 94 changed files with 528 additions and 208 deletions.
2 changes: 1 addition & 1 deletion ElementX.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8509,7 +8509,7 @@
repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift";
requirement = {
kind = exactVersion;
version = 25.02.06;
version = 25.02.07;
};
};
701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/element-hq/matrix-rust-components-swift",
"state" : {
"revision" : "355364952fdd14a3e26b317180af89c79f9e03a5",
"version" : "25.2.6"
"revision" : "bc819f09ac66bbe1adc2fde2afeb7ab023d1b909",
"version" : "25.2.7"
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,9 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
}

let parameters = SessionVerificationScreenCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationController,
flow: .initiator)
flow: .deviceInitiator,
appSettings: appSettings,
mediaProvider: userSession.mediaProvider)

let coordinator = SessionVerificationScreenCoordinator(parameters: parameters)

Expand Down
7 changes: 7 additions & 0 deletions ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import UserNotifications

enum RoomFlowCoordinatorAction: Equatable {
case presentCallScreen(roomProxy: JoinedRoomProxyProtocol)
case verifyUser(userID: String)
case finished

static func == (lhs: RoomFlowCoordinatorAction, rhs: RoomFlowCoordinatorAction) -> Bool {
Expand Down Expand Up @@ -1247,6 +1248,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
stateMachine.tryEvent(.startChildFlow(roomID: roomID, via: [], entryPoint: .room))
case .startCall(let roomID):
Task { await self.presentCallScreen(roomID: roomID) }
case .verifyUser(let userID):
actionsSubject.send(.verifyUser(userID: userID))
}
}
.store(in: &cancellables)
Expand All @@ -1272,6 +1275,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
stateMachine.tryEvent(.startChildFlow(roomID: roomID, via: [], entryPoint: .room))
case .startCall(let roomID):
Task { await self.presentCallScreen(roomID: roomID) }
case .verifyUser(let userID):
actionsSubject.send(.verifyUser(userID: userID))
case .dismiss:
break // Not supported when pushed.
}
Expand Down Expand Up @@ -1530,6 +1535,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
switch action {
case .presentCallScreen(let roomProxy):
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
case .verifyUser(let userID):
actionsSubject.send(.verifyUser(userID: userID))
case .finished:
stateMachine.tryEvent(.dismissChildFlow)
}
Expand Down
22 changes: 18 additions & 4 deletions ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -440,18 +440,26 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {

MXLog.info("Received session verification request")

presentSessionVerificationScreen(details: details)
if details.senderProfile.userID == userSession.clientProxy.userID {
presentSessionVerificationScreen(flow: .deviceResponder(requestDetails: details))
} else {
presentSessionVerificationScreen(flow: .userResponder(requestDetails: details))
}
}
.store(in: &cancellables)
}

private func presentSessionVerificationScreen(details: SessionVerificationRequestDetails) {
private func presentSessionVerificationScreen(flow: SessionVerificationScreenFlow) {
guard let sessionVerificationController = userSession.clientProxy.sessionVerificationController else {
fatalError("The sessionVerificationController should aways be valid at this point")
}

let navigationStackCoordinator = NavigationStackCoordinator()

let parameters = SessionVerificationScreenCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationController,
flow: .responder(details: details))
flow: flow,
appSettings: appSettings,
mediaProvider: userSession.mediaProvider)

let coordinator = SessionVerificationScreenCoordinator(parameters: parameters)

Expand All @@ -464,7 +472,9 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
}
.store(in: &cancellables)

navigationSplitCoordinator.setSheetCoordinator(coordinator)
navigationStackCoordinator.setRootCoordinator(coordinator)

navigationSplitCoordinator.setSheetCoordinator(navigationStackCoordinator)
}

private func presentHomeScreen() {
Expand Down Expand Up @@ -590,6 +600,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
case .presentCallScreen(let roomProxy):
// Here we assume that the app is running and the call state is already up to date
presentCallScreen(roomProxy: roomProxy, notifyOtherParticipants: !roomProxy.infoPublisher.value.hasRoomCall)
case .verifyUser(let userID):
presentSessionVerificationScreen(flow: .userIntiator(userID: userID))
case .finished:
stateMachine.processEvent(.deselectRoom)
}
Expand Down Expand Up @@ -911,6 +923,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
stateMachine.processEvent(.selectRoom(roomID: roomID, via: [], entryPoint: .room))
case .startCall(let roomID):
Task { await self.presentCallScreen(roomID: roomID, notifyOtherParticipants: false) }
case .verifyUser(let userID):
presentSessionVerificationScreen(flow: .userIntiator(userID: userID))
case .dismiss:
navigationSplitCoordinator.setSheetCoordinator(nil)
}
Expand Down
112 changes: 91 additions & 21 deletions ElementX/Sources/Mocks/Generated/GeneratedMocks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14105,68 +14105,138 @@ class SessionVerificationControllerProxyMock: SessionVerificationControllerProxy
return acceptVerificationRequestReturnValue
}
}
//MARK: - requestVerification
//MARK: - requestDeviceVerification

var requestVerificationUnderlyingCallsCount = 0
var requestVerificationCallsCount: Int {
var requestDeviceVerificationUnderlyingCallsCount = 0
var requestDeviceVerificationCallsCount: Int {
get {
if Thread.isMainThread {
return requestVerificationUnderlyingCallsCount
return requestDeviceVerificationUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = requestVerificationUnderlyingCallsCount
returnValue = requestDeviceVerificationUnderlyingCallsCount
}

return returnValue!
}
}
set {
if Thread.isMainThread {
requestVerificationUnderlyingCallsCount = newValue
requestDeviceVerificationUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
requestVerificationUnderlyingCallsCount = newValue
requestDeviceVerificationUnderlyingCallsCount = newValue
}
}
}
}
var requestVerificationCalled: Bool {
return requestVerificationCallsCount > 0
var requestDeviceVerificationCalled: Bool {
return requestDeviceVerificationCallsCount > 0
}

var requestVerificationUnderlyingReturnValue: Result<Void, SessionVerificationControllerProxyError>!
var requestVerificationReturnValue: Result<Void, SessionVerificationControllerProxyError>! {
var requestDeviceVerificationUnderlyingReturnValue: Result<Void, SessionVerificationControllerProxyError>!
var requestDeviceVerificationReturnValue: Result<Void, SessionVerificationControllerProxyError>! {
get {
if Thread.isMainThread {
return requestVerificationUnderlyingReturnValue
return requestDeviceVerificationUnderlyingReturnValue
} else {
var returnValue: Result<Void, SessionVerificationControllerProxyError>? = nil
DispatchQueue.main.sync {
returnValue = requestVerificationUnderlyingReturnValue
returnValue = requestDeviceVerificationUnderlyingReturnValue
}

return returnValue!
}
}
set {
if Thread.isMainThread {
requestVerificationUnderlyingReturnValue = newValue
requestDeviceVerificationUnderlyingReturnValue = newValue
} else {
DispatchQueue.main.sync {
requestVerificationUnderlyingReturnValue = newValue
requestDeviceVerificationUnderlyingReturnValue = newValue
}
}
}
}
var requestVerificationClosure: (() async -> Result<Void, SessionVerificationControllerProxyError>)?
var requestDeviceVerificationClosure: (() async -> Result<Void, SessionVerificationControllerProxyError>)?

func requestVerification() async -> Result<Void, SessionVerificationControllerProxyError> {
requestVerificationCallsCount += 1
if let requestVerificationClosure = requestVerificationClosure {
return await requestVerificationClosure()
func requestDeviceVerification() async -> Result<Void, SessionVerificationControllerProxyError> {
requestDeviceVerificationCallsCount += 1
if let requestDeviceVerificationClosure = requestDeviceVerificationClosure {
return await requestDeviceVerificationClosure()
} else {
return requestVerificationReturnValue
return requestDeviceVerificationReturnValue
}
}
//MARK: - requestUserVerification

var requestUserVerificationUnderlyingCallsCount = 0
var requestUserVerificationCallsCount: Int {
get {
if Thread.isMainThread {
return requestUserVerificationUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = requestUserVerificationUnderlyingCallsCount
}

return returnValue!
}
}
set {
if Thread.isMainThread {
requestUserVerificationUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
requestUserVerificationUnderlyingCallsCount = newValue
}
}
}
}
var requestUserVerificationCalled: Bool {
return requestUserVerificationCallsCount > 0
}
var requestUserVerificationReceivedUserID: String?
var requestUserVerificationReceivedInvocations: [String] = []

var requestUserVerificationUnderlyingReturnValue: Result<Void, SessionVerificationControllerProxyError>!
var requestUserVerificationReturnValue: Result<Void, SessionVerificationControllerProxyError>! {
get {
if Thread.isMainThread {
return requestUserVerificationUnderlyingReturnValue
} else {
var returnValue: Result<Void, SessionVerificationControllerProxyError>? = nil
DispatchQueue.main.sync {
returnValue = requestUserVerificationUnderlyingReturnValue
}

return returnValue!
}
}
set {
if Thread.isMainThread {
requestUserVerificationUnderlyingReturnValue = newValue
} else {
DispatchQueue.main.sync {
requestUserVerificationUnderlyingReturnValue = newValue
}
}
}
}
var requestUserVerificationClosure: ((String) async -> Result<Void, SessionVerificationControllerProxyError>)?

func requestUserVerification(_ userID: String) async -> Result<Void, SessionVerificationControllerProxyError> {
requestUserVerificationCallsCount += 1
requestUserVerificationReceivedUserID = userID
DispatchQueue.main.async {
self.requestUserVerificationReceivedInvocations.append(userID)
}
if let requestUserVerificationClosure = requestUserVerificationClosure {
return await requestUserVerificationClosure(userID)
} else {
return requestUserVerificationReturnValue
}
}
//MARK: - startSasVerification
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ extension SessionVerificationControllerProxyMock {

mock.acknowledgeVerificationRequestDetailsReturnValue = .success(())

mock.requestVerificationClosure = { [unowned mock] in
mock.requestDeviceVerificationClosure = { [unowned mock] in
Task.detached {
try await Task.sleep(for: requestDelay)
mock.actions.send(.acceptedVerificationRequest)
Expand Down
3 changes: 3 additions & 0 deletions ElementX/Sources/Other/Avatars.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ enum UserAvatarSizeOnScreen {
case knockingUserList
case mediaPreviewDetails
case sendInviteConfirmation
case sessionVerification

var value: CGFloat {
switch self {
Expand Down Expand Up @@ -116,6 +117,8 @@ enum UserAvatarSizeOnScreen {
return 32
case .sendInviteConfirmation:
return 64
case .sessionVerification:
return 52
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,26 @@ enum SessionVerificationScreenCoordinatorAction {
}

enum SessionVerificationScreenFlow {
case initiator
case responder(details: SessionVerificationRequestDetails)
case deviceInitiator
case deviceResponder(requestDetails: SessionVerificationRequestDetails)
case userIntiator(userID: String)
case userResponder(requestDetails: SessionVerificationRequestDetails)

var isResponder: Bool {
switch self {
case .deviceInitiator, .userIntiator:
false
case .deviceResponder, .userResponder:
true
}
}
}

struct SessionVerificationScreenCoordinatorParameters {
let sessionVerificationControllerProxy: SessionVerificationControllerProxyProtocol
let flow: SessionVerificationScreenFlow
let appSettings: AppSettings
let mediaProvider: MediaProviderProtocol
}

final class SessionVerificationScreenCoordinator: CoordinatorProtocol {
Expand All @@ -35,7 +48,9 @@ final class SessionVerificationScreenCoordinator: CoordinatorProtocol {

init(parameters: SessionVerificationScreenCoordinatorParameters) {
viewModel = SessionVerificationScreenViewModel(sessionVerificationControllerProxy: parameters.sessionVerificationControllerProxy,
flow: parameters.flow)
flow: parameters.flow,
appSettings: parameters.appSettings,
mediaProvider: parameters.mediaProvider)
}

// MARK: - Public
Expand Down
Loading

0 comments on commit 8680d84

Please sign in to comment.