mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 20:09:04 +00:00
Add the JoinRoomScreen into the SpaceFlowCoordinator. (#4513)
* Add the JoinRoomScreen into the SpaceFlowCoordinator. The screen needs further work to make the join work. * Fix the tests completing the join flow with the right action.
This commit is contained in:
@@ -703,7 +703,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case .joined:
|
case .joined(.roomID(let roomID)):
|
||||||
Task { [weak self] in
|
Task { [weak self] in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
|
|
||||||
@@ -718,6 +718,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
stateMachine.tryEvent(.dismissFlow, userInfo: EventUserInfo(animated: animated))
|
stateMachine.tryEvent(.dismissFlow, userInfo: EventUserInfo(animated: animated))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case .joined(.space):
|
||||||
|
#warning("The space flow should be shown here.")
|
||||||
case .cancelled:
|
case .cancelled:
|
||||||
stateMachine.tryEvent(.dismissJoinRoomScreen)
|
stateMachine.tryEvent(.dismissJoinRoomScreen)
|
||||||
case .presentDeclineAndBlock(let userID):
|
case .presentDeclineAndBlock(let userID):
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ class SpaceExplorerFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func startSpaceFlow(spaceRoomListProxy: SpaceRoomListProxyProtocol) {
|
private func startSpaceFlow(spaceRoomListProxy: SpaceRoomListProxyProtocol) {
|
||||||
let coordinator = SpaceFlowCoordinator(spaceRoomListProxy: spaceRoomListProxy,
|
let coordinator = SpaceFlowCoordinator(entryPoint: .space(spaceRoomListProxy),
|
||||||
spaceServiceProxy: userSession.clientProxy.spaceService,
|
spaceServiceProxy: userSession.clientProxy.spaceService,
|
||||||
isChildFlow: false,
|
isChildFlow: false,
|
||||||
navigationStackCoordinator: detailNavigationStackCoordinator,
|
navigationStackCoordinator: detailNavigationStackCoordinator,
|
||||||
|
|||||||
@@ -15,8 +15,20 @@ enum SpaceFlowCoordinatorAction {
|
|||||||
case finished
|
case finished
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum SpaceFlowCoordinatorEntryPoint {
|
||||||
|
case space(SpaceRoomListProxyProtocol)
|
||||||
|
case joinSpace(SpaceRoomProxyProtocol)
|
||||||
|
|
||||||
|
var spaceID: String {
|
||||||
|
switch self {
|
||||||
|
case .space(let spaceRoomListProxy): spaceRoomListProxy.spaceRoomProxy.id
|
||||||
|
case .joinSpace(let spaceRoomProxy): spaceRoomProxy.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
||||||
private let spaceRoomListProxy: SpaceRoomListProxyProtocol
|
private var entryPoint: SpaceFlowCoordinatorEntryPoint
|
||||||
private let spaceServiceProxy: SpaceServiceProxyProtocol
|
private let spaceServiceProxy: SpaceServiceProxyProtocol
|
||||||
private let isChildFlow: Bool
|
private let isChildFlow: Bool
|
||||||
|
|
||||||
@@ -32,6 +44,8 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
indirect enum State: StateType {
|
indirect enum State: StateType {
|
||||||
/// The state machine hasn't started.
|
/// The state machine hasn't started.
|
||||||
case initial
|
case initial
|
||||||
|
/// Shown when the flow is started for an unjoined space.
|
||||||
|
case joinSpace
|
||||||
/// The root screen for this flow.
|
/// The root screen for this flow.
|
||||||
case space
|
case space
|
||||||
/// A child (space) flow is in progress.
|
/// A child (space) flow is in progress.
|
||||||
@@ -43,6 +57,11 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
enum Event: EventType {
|
enum Event: EventType {
|
||||||
/// The flow is being started.
|
/// The flow is being started.
|
||||||
case start
|
case start
|
||||||
|
/// The flow is being started for an unjoined space.
|
||||||
|
case startUnjoined
|
||||||
|
|
||||||
|
/// The join space screen joined the space.
|
||||||
|
case joinedSpace
|
||||||
|
|
||||||
/// Request the presentation of a child space flow.
|
/// Request the presentation of a child space flow.
|
||||||
///
|
///
|
||||||
@@ -63,12 +82,12 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
actionsSubject.eraseToAnyPublisher()
|
actionsSubject.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
init(spaceRoomListProxy: SpaceRoomListProxyProtocol,
|
init(entryPoint: SpaceFlowCoordinatorEntryPoint,
|
||||||
spaceServiceProxy: SpaceServiceProxyProtocol,
|
spaceServiceProxy: SpaceServiceProxyProtocol,
|
||||||
isChildFlow: Bool,
|
isChildFlow: Bool,
|
||||||
navigationStackCoordinator: NavigationStackCoordinator,
|
navigationStackCoordinator: NavigationStackCoordinator,
|
||||||
flowParameters: CommonFlowParameters) {
|
flowParameters: CommonFlowParameters) {
|
||||||
self.spaceRoomListProxy = spaceRoomListProxy
|
self.entryPoint = entryPoint
|
||||||
self.spaceServiceProxy = spaceServiceProxy
|
self.spaceServiceProxy = spaceServiceProxy
|
||||||
self.isChildFlow = isChildFlow
|
self.isChildFlow = isChildFlow
|
||||||
self.flowParameters = flowParameters
|
self.flowParameters = flowParameters
|
||||||
@@ -80,7 +99,12 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
stateMachine.tryEvent(.start)
|
switch entryPoint {
|
||||||
|
case .space:
|
||||||
|
stateMachine.tryEvent(.start)
|
||||||
|
case .joinSpace:
|
||||||
|
stateMachine.tryEvent(.startUnjoined)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
||||||
@@ -92,7 +116,7 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
switch stateMachine.state {
|
switch stateMachine.state {
|
||||||
case .initial:
|
case .initial:
|
||||||
break
|
break
|
||||||
case .space:
|
case .joinSpace, .space:
|
||||||
if isChildFlow {
|
if isChildFlow {
|
||||||
navigationStackCoordinator.pop(animated: animated)
|
navigationStackCoordinator.pop(animated: animated)
|
||||||
} else {
|
} else {
|
||||||
@@ -114,13 +138,21 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
self?.presentSpace()
|
self?.presentSpace()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stateMachine.addRoutes(event: .startUnjoined, transitions: [.initial => .joinSpace]) { [weak self] _ in
|
||||||
|
self?.presentJoinSpaceScreen()
|
||||||
|
}
|
||||||
|
|
||||||
|
stateMachine.addRoutes(event: .joinedSpace, transitions: [.joinSpace => .space]) { [weak self] _ in
|
||||||
|
self?.presentSpaceAfterJoining()
|
||||||
|
}
|
||||||
|
|
||||||
stateMachine.addRouteMapping { event, fromState, userInfo in
|
stateMachine.addRouteMapping { event, fromState, userInfo in
|
||||||
guard event == .startChildFlow, case .space = fromState else { return nil }
|
guard event == .startChildFlow, case .space = fromState else { return nil }
|
||||||
guard let spaceRoomListProxy = userInfo as? SpaceRoomListProxyProtocol else { fatalError("A space proxy must be provided.") }
|
guard let childEntryPoint = userInfo as? SpaceFlowCoordinatorEntryPoint else { fatalError("An entry point must be provided.") }
|
||||||
return .presentingChild(childSpaceID: spaceRoomListProxy.spaceRoomProxy.id, previousState: fromState)
|
return .presentingChild(childSpaceID: childEntryPoint.spaceID, previousState: fromState)
|
||||||
} handler: { [weak self] context in
|
} handler: { [weak self] context in
|
||||||
guard let self, let spaceRoomListProxy = context.userInfo as? SpaceRoomListProxyProtocol else { return }
|
guard let self, let entryPoint = context.userInfo as? SpaceFlowCoordinatorEntryPoint else { return }
|
||||||
startChildFlow(for: spaceRoomListProxy)
|
startChildFlow(with: entryPoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
stateMachine.addRouteMapping { event, fromState, _ in
|
stateMachine.addRouteMapping { event, fromState, _ in
|
||||||
@@ -155,6 +187,8 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func presentSpace() {
|
private func presentSpace() {
|
||||||
|
guard case let .space(spaceRoomListProxy) = entryPoint else { fatalError("Attempting to show a space with the wrong entry point.") }
|
||||||
|
|
||||||
let parameters = SpaceScreenCoordinatorParameters(spaceRoomListProxy: spaceRoomListProxy,
|
let parameters = SpaceScreenCoordinatorParameters(spaceRoomListProxy: spaceRoomListProxy,
|
||||||
spaceServiceProxy: spaceServiceProxy,
|
spaceServiceProxy: spaceServiceProxy,
|
||||||
selectedSpaceRoomPublisher: selectedSpaceRoomSubject.asCurrentValuePublisher(),
|
selectedSpaceRoomPublisher: selectedSpaceRoomSubject.asCurrentValuePublisher(),
|
||||||
@@ -166,7 +200,9 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
switch action {
|
switch action {
|
||||||
case .selectSpace(let spaceRoomListProxy):
|
case .selectSpace(let spaceRoomListProxy):
|
||||||
stateMachine.tryEvent(.startChildFlow, userInfo: spaceRoomListProxy)
|
stateMachine.tryEvent(.startChildFlow, userInfo: SpaceFlowCoordinatorEntryPoint.space(spaceRoomListProxy))
|
||||||
|
case .selectUnjoinedSpace(let spaceRoomProxy):
|
||||||
|
stateMachine.tryEvent(.startChildFlow, userInfo: SpaceFlowCoordinatorEntryPoint.joinSpace(spaceRoomProxy))
|
||||||
case .selectRoom(let roomID):
|
case .selectRoom(let roomID):
|
||||||
stateMachine.tryEvent(.startRoomFlow(roomID: roomID))
|
stateMachine.tryEvent(.startRoomFlow(roomID: roomID))
|
||||||
}
|
}
|
||||||
@@ -184,10 +220,61 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func presentJoinSpaceScreen() {
|
||||||
|
guard case let .joinSpace(spaceRoomProxy) = entryPoint else { fatalError("Attempting to join a space with the wrong entry point.") }
|
||||||
|
|
||||||
|
let parameters = JoinRoomScreenCoordinatorParameters(roomID: spaceRoomProxy.id,
|
||||||
|
via: [],
|
||||||
|
userSession: flowParameters.userSession,
|
||||||
|
userIndicatorController: flowParameters.userIndicatorController,
|
||||||
|
appSettings: flowParameters.appSettings)
|
||||||
|
let coordinator = JoinRoomScreenCoordinator(parameters: parameters)
|
||||||
|
coordinator.actionsPublisher
|
||||||
|
.sink { [weak self] action in
|
||||||
|
guard let self else { return }
|
||||||
|
switch action {
|
||||||
|
case .joined(.space(let spaceRoomListProxy)):
|
||||||
|
entryPoint = .space(spaceRoomListProxy)
|
||||||
|
stateMachine.tryEvent(.joinedSpace)
|
||||||
|
case .joined(.roomID):
|
||||||
|
MXLog.error("Expected to join a space, but got a room ID instead.")
|
||||||
|
clearRoute(animated: true)
|
||||||
|
case .cancelled:
|
||||||
|
clearRoute(animated: true)
|
||||||
|
case .presentDeclineAndBlock:
|
||||||
|
MXLog.error("Joining a space from the spaces tab shouldn't involve an inviter.")
|
||||||
|
clearRoute(animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
if isChildFlow {
|
||||||
|
navigationStackCoordinator.push(coordinator) { [weak self] in
|
||||||
|
guard let self, stateMachine.state == .joinSpace else { return }
|
||||||
|
actionsSubject.send(.finished)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
navigationStackCoordinator.setRootCoordinator(coordinator) { [weak self] in
|
||||||
|
guard let self, stateMachine.state == .joinSpace else { return }
|
||||||
|
actionsSubject.send(.finished)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func presentSpaceAfterJoining() {
|
||||||
|
if isChildFlow {
|
||||||
|
navigationStackCoordinator.pop()
|
||||||
|
} else {
|
||||||
|
navigationStackCoordinator.setRootCoordinator(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
presentSpace()
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Other flows
|
// MARK: - Other flows
|
||||||
|
|
||||||
private func startChildFlow(for spaceRoomListProxy: SpaceRoomListProxyProtocol) {
|
private func startChildFlow(with entryPoint: SpaceFlowCoordinatorEntryPoint) {
|
||||||
let coordinator = SpaceFlowCoordinator(spaceRoomListProxy: spaceRoomListProxy,
|
let coordinator = SpaceFlowCoordinator(entryPoint: entryPoint,
|
||||||
spaceServiceProxy: spaceServiceProxy,
|
spaceServiceProxy: spaceServiceProxy,
|
||||||
isChildFlow: true,
|
isChildFlow: true,
|
||||||
navigationStackCoordinator: navigationStackCoordinator,
|
navigationStackCoordinator: navigationStackCoordinator,
|
||||||
@@ -210,7 +297,7 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
childSpaceFlowCoordinator = coordinator
|
childSpaceFlowCoordinator = coordinator
|
||||||
coordinator.start()
|
coordinator.start()
|
||||||
selectedSpaceRoomSubject.send(spaceRoomListProxy.spaceRoomProxy.id)
|
selectedSpaceRoomSubject.send(entryPoint.spaceID)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func startRoomFlow(roomID: String) {
|
private func startRoomFlow(roomID: String) {
|
||||||
|
|||||||
@@ -15900,15 +15900,15 @@ class SpaceServiceProxyMock: SpaceServiceProxyProtocol, @unchecked Sendable {
|
|||||||
|
|
||||||
//MARK: - spaceRoomList
|
//MARK: - spaceRoomList
|
||||||
|
|
||||||
var spaceRoomListForUnderlyingCallsCount = 0
|
var spaceRoomListSpaceIDUnderlyingCallsCount = 0
|
||||||
var spaceRoomListForCallsCount: Int {
|
var spaceRoomListSpaceIDCallsCount: Int {
|
||||||
get {
|
get {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
return spaceRoomListForUnderlyingCallsCount
|
return spaceRoomListSpaceIDUnderlyingCallsCount
|
||||||
} else {
|
} else {
|
||||||
var returnValue: Int? = nil
|
var returnValue: Int? = nil
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
returnValue = spaceRoomListForUnderlyingCallsCount
|
returnValue = spaceRoomListSpaceIDUnderlyingCallsCount
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnValue!
|
return returnValue!
|
||||||
@@ -15916,29 +15916,29 @@ class SpaceServiceProxyMock: SpaceServiceProxyProtocol, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
spaceRoomListForUnderlyingCallsCount = newValue
|
spaceRoomListSpaceIDUnderlyingCallsCount = newValue
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
spaceRoomListForUnderlyingCallsCount = newValue
|
spaceRoomListSpaceIDUnderlyingCallsCount = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var spaceRoomListForCalled: Bool {
|
var spaceRoomListSpaceIDCalled: Bool {
|
||||||
return spaceRoomListForCallsCount > 0
|
return spaceRoomListSpaceIDCallsCount > 0
|
||||||
}
|
}
|
||||||
var spaceRoomListForReceivedSpaceRoomProxy: SpaceRoomProxyProtocol?
|
var spaceRoomListSpaceIDReceivedSpaceID: String?
|
||||||
var spaceRoomListForReceivedInvocations: [SpaceRoomProxyProtocol] = []
|
var spaceRoomListSpaceIDReceivedInvocations: [String] = []
|
||||||
|
|
||||||
var spaceRoomListForUnderlyingReturnValue: Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>!
|
var spaceRoomListSpaceIDUnderlyingReturnValue: Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>!
|
||||||
var spaceRoomListForReturnValue: Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>! {
|
var spaceRoomListSpaceIDReturnValue: Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>! {
|
||||||
get {
|
get {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
return spaceRoomListForUnderlyingReturnValue
|
return spaceRoomListSpaceIDUnderlyingReturnValue
|
||||||
} else {
|
} else {
|
||||||
var returnValue: Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>? = nil
|
var returnValue: Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>? = nil
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
returnValue = spaceRoomListForUnderlyingReturnValue
|
returnValue = spaceRoomListSpaceIDUnderlyingReturnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnValue!
|
return returnValue!
|
||||||
@@ -15946,26 +15946,26 @@ class SpaceServiceProxyMock: SpaceServiceProxyProtocol, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
spaceRoomListForUnderlyingReturnValue = newValue
|
spaceRoomListSpaceIDUnderlyingReturnValue = newValue
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
spaceRoomListForUnderlyingReturnValue = newValue
|
spaceRoomListSpaceIDUnderlyingReturnValue = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var spaceRoomListForClosure: ((SpaceRoomProxyProtocol) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>)?
|
var spaceRoomListSpaceIDClosure: ((String) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>)?
|
||||||
|
|
||||||
func spaceRoomList(for spaceRoomProxy: SpaceRoomProxyProtocol) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError> {
|
func spaceRoomList(spaceID: String) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError> {
|
||||||
spaceRoomListForCallsCount += 1
|
spaceRoomListSpaceIDCallsCount += 1
|
||||||
spaceRoomListForReceivedSpaceRoomProxy = spaceRoomProxy
|
spaceRoomListSpaceIDReceivedSpaceID = spaceID
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.spaceRoomListForReceivedInvocations.append(spaceRoomProxy)
|
self.spaceRoomListSpaceIDReceivedInvocations.append(spaceID)
|
||||||
}
|
}
|
||||||
if let spaceRoomListForClosure = spaceRoomListForClosure {
|
if let spaceRoomListSpaceIDClosure = spaceRoomListSpaceIDClosure {
|
||||||
return await spaceRoomListForClosure(spaceRoomProxy)
|
return await spaceRoomListSpaceIDClosure(spaceID)
|
||||||
} else {
|
} else {
|
||||||
return spaceRoomListForReturnValue
|
return spaceRoomListSpaceIDReturnValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ extension SpaceServiceProxyMock {
|
|||||||
self.init()
|
self.init()
|
||||||
|
|
||||||
joinedSpacesPublisher = .init(configuration.joinedSpaces)
|
joinedSpacesPublisher = .init(configuration.joinedSpaces)
|
||||||
spaceRoomListForClosure = { spaceRoomProxy in
|
spaceRoomListSpaceIDClosure = { spaceID in
|
||||||
if let spaceRoomList = configuration.spaceRoomLists[spaceRoomProxy.id] {
|
if let spaceRoomList = configuration.spaceRoomLists[spaceID] {
|
||||||
.success(spaceRoomList)
|
.success(spaceRoomList)
|
||||||
} else {
|
} else {
|
||||||
.failure(.sdkError(ClientProxyMockError.generic))
|
.failure(.sdkError(ClientProxyMockError.generic))
|
||||||
|
|||||||
@@ -16,8 +16,13 @@ struct JoinRoomScreenCoordinatorParameters {
|
|||||||
let appSettings: AppSettings
|
let appSettings: AppSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum JoinRoomScreenJoinDetails {
|
||||||
|
case roomID(String)
|
||||||
|
case space(SpaceRoomListProxyProtocol)
|
||||||
|
}
|
||||||
|
|
||||||
enum JoinRoomScreenCoordinatorAction {
|
enum JoinRoomScreenCoordinatorAction {
|
||||||
case joined
|
case joined(JoinRoomScreenJoinDetails)
|
||||||
case cancelled
|
case cancelled
|
||||||
case presentDeclineAndBlock(userID: String)
|
case presentDeclineAndBlock(userID: String)
|
||||||
}
|
}
|
||||||
@@ -46,8 +51,8 @@ final class JoinRoomScreenCoordinator: CoordinatorProtocol {
|
|||||||
|
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
switch action {
|
switch action {
|
||||||
case .joined:
|
case .joined(let details):
|
||||||
actionsSubject.send(.joined)
|
actionsSubject.send(.joined(details))
|
||||||
case .dismiss:
|
case .dismiss:
|
||||||
actionsSubject.send(.cancelled)
|
actionsSubject.send(.cancelled)
|
||||||
case .presentDeclineAndBlock(let userID):
|
case .presentDeclineAndBlock(let userID):
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum JoinRoomScreenViewModelAction: Equatable {
|
enum JoinRoomScreenViewModelAction {
|
||||||
case joined
|
case joined(JoinRoomScreenJoinDetails)
|
||||||
case dismiss
|
case dismiss
|
||||||
case presentDeclineAndBlock(userID: String)
|
case presentDeclineAndBlock(userID: String)
|
||||||
}
|
}
|
||||||
@@ -43,6 +43,7 @@ struct JoinRoomScreenRoomDetails {
|
|||||||
let memberCount: Int?
|
let memberCount: Int?
|
||||||
let inviter: RoomInviterDetails?
|
let inviter: RoomInviterDetails?
|
||||||
let isDirect: Bool?
|
let isDirect: Bool?
|
||||||
|
let isSpace: Bool?
|
||||||
}
|
}
|
||||||
|
|
||||||
struct JoinRoomScreenViewState: BindableState {
|
struct JoinRoomScreenViewState: BindableState {
|
||||||
|
|||||||
@@ -179,7 +179,8 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo
|
|||||||
avatar: avatar,
|
avatar: avatar,
|
||||||
memberCount: info?.joinedMembersCount,
|
memberCount: info?.joinedMembersCount,
|
||||||
inviter: inviter,
|
inviter: inviter,
|
||||||
isDirect: info?.isDirect)
|
isDirect: info?.isDirect,
|
||||||
|
isSpace: info?.isSpace)
|
||||||
|
|
||||||
await updateMode()
|
await updateMode()
|
||||||
}
|
}
|
||||||
@@ -243,8 +244,7 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo
|
|||||||
if let alias = state.roomDetails?.canonicalAlias {
|
if let alias = state.roomDetails?.canonicalAlias {
|
||||||
switch await clientProxy.joinRoomAlias(alias) {
|
switch await clientProxy.joinRoomAlias(alias) {
|
||||||
case .success:
|
case .success:
|
||||||
appSettings.seenInvites.remove(roomID)
|
await finishJoinAction()
|
||||||
actionsSubject.send(.joined)
|
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
switch error {
|
switch error {
|
||||||
case .forbiddenAccess:
|
case .forbiddenAccess:
|
||||||
@@ -261,8 +261,7 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo
|
|||||||
} else {
|
} else {
|
||||||
switch await clientProxy.joinRoom(roomID, via: via) {
|
switch await clientProxy.joinRoom(roomID, via: via) {
|
||||||
case .success:
|
case .success:
|
||||||
appSettings.seenInvites.remove(roomID)
|
await finishJoinAction()
|
||||||
actionsSubject.send(.joined)
|
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
switch error {
|
switch error {
|
||||||
case .forbiddenAccess:
|
case .forbiddenAccess:
|
||||||
@@ -279,6 +278,23 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func finishJoinAction() async {
|
||||||
|
appSettings.seenInvites.remove(roomID)
|
||||||
|
|
||||||
|
guard state.roomDetails?.isSpace == true else {
|
||||||
|
actionsSubject.send(.joined(.roomID(roomID)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch await clientProxy.spaceService.spaceRoomList(spaceID: roomID) {
|
||||||
|
case .success(let spaceRoomListProxy):
|
||||||
|
actionsSubject.send(.joined(.space(spaceRoomListProxy)))
|
||||||
|
case .failure(let error):
|
||||||
|
MXLog.error("Failed to get the space room list after joining: \(error)")
|
||||||
|
userIndicatorController.submitIndicator(.init(title: L10n.errorUnknown))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func knockRoom() async {
|
private func knockRoom() async {
|
||||||
showLoadingIndicator()
|
showLoadingIndicator()
|
||||||
|
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ class SpaceListScreenViewModel: SpaceListScreenViewModelType, SpaceListScreenVie
|
|||||||
switch viewAction {
|
switch viewAction {
|
||||||
case .spaceAction(.select(let spaceRoomProxy)):
|
case .spaceAction(.select(let spaceRoomProxy)):
|
||||||
Task { await selectSpace(spaceRoomProxy) }
|
Task { await selectSpace(spaceRoomProxy) }
|
||||||
case .spaceAction(.join(let spaceRoomProxy)):
|
case .spaceAction(.join):
|
||||||
#warning("Implement joining.")
|
fatalError("There shouldn't be any unjoined spaces in the joined spaces list.")
|
||||||
case .showSettings:
|
case .showSettings:
|
||||||
actionsSubject.send(.showSettings)
|
actionsSubject.send(.showSettings)
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ class SpaceListScreenViewModel: SpaceListScreenViewModelType, SpaceListScreenVie
|
|||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func selectSpace(_ spaceRoomProxy: SpaceRoomProxyProtocol) async {
|
private func selectSpace(_ spaceRoomProxy: SpaceRoomProxyProtocol) async {
|
||||||
switch await spaceServiceProxy.spaceRoomList(for: spaceRoomProxy) {
|
switch await spaceServiceProxy.spaceRoomList(spaceID: spaceRoomProxy.id) {
|
||||||
case .success(let spaceRoomListProxy):
|
case .success(let spaceRoomListProxy):
|
||||||
actionsSubject.send(.selectSpace(spaceRoomListProxy))
|
actionsSubject.send(.selectSpace(spaceRoomListProxy))
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ struct SpaceScreenCoordinatorParameters {
|
|||||||
|
|
||||||
enum SpaceScreenCoordinatorAction {
|
enum SpaceScreenCoordinatorAction {
|
||||||
case selectSpace(SpaceRoomListProxyProtocol)
|
case selectSpace(SpaceRoomListProxyProtocol)
|
||||||
|
case selectUnjoinedSpace(SpaceRoomProxyProtocol)
|
||||||
case selectRoom(roomID: String)
|
case selectRoom(roomID: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,6 +53,8 @@ final class SpaceScreenCoordinator: CoordinatorProtocol {
|
|||||||
switch action {
|
switch action {
|
||||||
case .selectSpace(let spaceRoomListProxy):
|
case .selectSpace(let spaceRoomListProxy):
|
||||||
actionsSubject.send(.selectSpace(spaceRoomListProxy))
|
actionsSubject.send(.selectSpace(spaceRoomListProxy))
|
||||||
|
case .selectUnjoinedSpace(let spaceRoomProxy):
|
||||||
|
actionsSubject.send(.selectUnjoinedSpace(spaceRoomProxy))
|
||||||
case .selectRoom(let roomID):
|
case .selectRoom(let roomID):
|
||||||
actionsSubject.send(.selectRoom(roomID: roomID))
|
actionsSubject.send(.selectRoom(roomID: roomID))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import Foundation
|
|||||||
|
|
||||||
enum SpaceScreenViewModelAction {
|
enum SpaceScreenViewModelAction {
|
||||||
case selectSpace(SpaceRoomListProxyProtocol)
|
case selectSpace(SpaceRoomListProxyProtocol)
|
||||||
|
case selectUnjoinedSpace(SpaceRoomProxyProtocol)
|
||||||
case selectRoom(roomID: String)
|
case selectRoom(roomID: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,11 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
|
|||||||
switch viewAction {
|
switch viewAction {
|
||||||
case .spaceAction(.select(let spaceRoomProxy)):
|
case .spaceAction(.select(let spaceRoomProxy)):
|
||||||
if spaceRoomProxy.isSpace {
|
if spaceRoomProxy.isSpace {
|
||||||
Task { await selectSpace(spaceRoomProxy) }
|
if spaceRoomProxy.state != .joined {
|
||||||
|
actionsSubject.send(.selectUnjoinedSpace(spaceRoomProxy))
|
||||||
|
} else {
|
||||||
|
Task { await selectSpace(spaceRoomProxy) }
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// No need to check the join state, the room flow will show an appropriately configured join screen if needed.
|
// No need to check the join state, the room flow will show an appropriately configured join screen if needed.
|
||||||
actionsSubject.send(.selectRoom(roomID: spaceRoomProxy.id))
|
actionsSubject.send(.selectRoom(roomID: spaceRoomProxy.id))
|
||||||
@@ -85,16 +89,6 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
|
|||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func selectSpace(_ spaceRoomProxy: SpaceRoomProxyProtocol) async {
|
|
||||||
switch await spaceServiceProxy.spaceRoomList(for: spaceRoomProxy) {
|
|
||||||
case .success(let spaceRoomListProxy):
|
|
||||||
actionsSubject.send(.selectSpace(spaceRoomListProxy))
|
|
||||||
case .failure(let error):
|
|
||||||
MXLog.error("Unable to select space: \(error)")
|
|
||||||
showFailureIndicator()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func join(_ spaceRoomProxy: SpaceRoomProxyProtocol) async {
|
private func join(_ spaceRoomProxy: SpaceRoomProxyProtocol) async {
|
||||||
state.joiningRoomIDs.insert(spaceRoomProxy.id)
|
state.joiningRoomIDs.insert(spaceRoomProxy.id)
|
||||||
defer { state.joiningRoomIDs.remove(spaceRoomProxy.id) }
|
defer { state.joiningRoomIDs.remove(spaceRoomProxy.id) }
|
||||||
@@ -114,6 +108,16 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func selectSpace(_ spaceRoomProxy: SpaceRoomProxyProtocol) async {
|
||||||
|
switch await spaceServiceProxy.spaceRoomList(spaceID: spaceRoomProxy.id) {
|
||||||
|
case .success(let spaceRoomListProxy):
|
||||||
|
actionsSubject.send(.selectSpace(spaceRoomListProxy))
|
||||||
|
case .failure(let error):
|
||||||
|
MXLog.error("Unable to select space: \(error)")
|
||||||
|
showFailureIndicator()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Indicators
|
// MARK: - Indicators
|
||||||
|
|
||||||
private static var failureIndicatorID: String { "\(Self.self)-Failure" }
|
private static var failureIndicatorID: String { "\(Self.self)-Failure" }
|
||||||
|
|||||||
@@ -21,9 +21,11 @@ class SpaceRoomListProxy: SpaceRoomListProxyProtocol {
|
|||||||
private let paginationStateHandle: TaskHandle
|
private let paginationStateHandle: TaskHandle
|
||||||
let paginationStatePublisher: CurrentValuePublisher<SpaceRoomListPaginationState, Never>
|
let paginationStatePublisher: CurrentValuePublisher<SpaceRoomListPaginationState, Never>
|
||||||
|
|
||||||
init(_ spaceRoomList: SpaceRoomListProtocol, spaceRoomProxy: SpaceRoomProxyProtocol) {
|
init(_ spaceRoomList: SpaceRoomListProtocol) throws {
|
||||||
|
guard let spaceRoom = spaceRoomList.space() else { throw SpaceRoomListProxyError.missingSpace }
|
||||||
|
|
||||||
self.spaceRoomList = spaceRoomList
|
self.spaceRoomList = spaceRoomList
|
||||||
self.spaceRoomProxy = spaceRoomProxy
|
spaceRoomProxy = SpaceRoomProxy(spaceRoom: spaceRoom)
|
||||||
|
|
||||||
let paginationStateSubject = CurrentValueSubject<SpaceRoomListPaginationState, Never>(spaceRoomList.paginationState())
|
let paginationStateSubject = CurrentValueSubject<SpaceRoomListPaginationState, Never>(spaceRoomList.paginationState())
|
||||||
paginationStatePublisher = paginationStateSubject.asCurrentValuePublisher()
|
paginationStatePublisher = paginationStateSubject.asCurrentValuePublisher()
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
import Combine
|
import Combine
|
||||||
import MatrixRustSDK
|
import MatrixRustSDK
|
||||||
|
|
||||||
|
enum SpaceRoomListProxyError: Error {
|
||||||
|
case missingSpace
|
||||||
|
}
|
||||||
|
|
||||||
// sourcery: AutoMockable
|
// sourcery: AutoMockable
|
||||||
protocol SpaceRoomListProxyProtocol {
|
protocol SpaceRoomListProxyProtocol {
|
||||||
var spaceRoomProxy: SpaceRoomProxyProtocol { get }
|
var spaceRoomProxy: SpaceRoomProxyProtocol { get }
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ class SpaceServiceProxy: SpaceServiceProxyProtocol {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func spaceRoomList(for spaceRoomProxy: SpaceRoomProxyProtocol) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError> {
|
func spaceRoomList(spaceID: String) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError> {
|
||||||
do {
|
do {
|
||||||
return try await .success(SpaceRoomListProxy(spaceService.spaceRoomList(spaceId: spaceRoomProxy.id), spaceRoomProxy: spaceRoomProxy))
|
return try await .success(SpaceRoomListProxy(spaceService.spaceRoomList(spaceId: spaceID)))
|
||||||
} catch {
|
} catch {
|
||||||
MXLog.error("Failed creating space room list for \(spaceRoomProxy.id): \(error)")
|
MXLog.error("Failed creating space room list for \(spaceID): \(error)")
|
||||||
return .failure(.sdkError(error))
|
return .failure(.sdkError(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,12 @@ import Foundation
|
|||||||
|
|
||||||
enum SpaceServiceProxyError: Error {
|
enum SpaceServiceProxyError: Error {
|
||||||
case sdkError(Error)
|
case sdkError(Error)
|
||||||
|
case missingSpace
|
||||||
}
|
}
|
||||||
|
|
||||||
// sourcery: AutoMockable
|
// sourcery: AutoMockable
|
||||||
protocol SpaceServiceProxyProtocol {
|
protocol SpaceServiceProxyProtocol {
|
||||||
var joinedSpacesPublisher: CurrentValuePublisher<[SpaceRoomProxyProtocol], Never> { get }
|
var joinedSpacesPublisher: CurrentValuePublisher<[SpaceRoomProxyProtocol], Never> { get }
|
||||||
|
|
||||||
func spaceRoomList(for spaceRoomProxy: SpaceRoomProxyProtocol) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>
|
func spaceRoomList(spaceID: String) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import XCTest
|
|||||||
class UserSessionScreenTests: XCTestCase {
|
class UserSessionScreenTests: XCTestCase {
|
||||||
let firstRoomName = "Foundation 🔭🪐🌌"
|
let firstRoomName = "Foundation 🔭🪐🌌"
|
||||||
let firstSpaceName = "The Foundation"
|
let firstSpaceName = "The Foundation"
|
||||||
let firstSpaceRoomName = "Company Room"
|
let unjoinedSpaceRoomName = "Company Room"
|
||||||
let firstSubspaceName = "Company Space"
|
let joinedSubspaceName = "Joined Space"
|
||||||
let firstSubspaceRoomName = "Management"
|
let joinedSubspaceRoomName = "Management"
|
||||||
|
|
||||||
enum Step {
|
enum Step {
|
||||||
static let homeScreen = 1
|
static let homeScreen = 1
|
||||||
@@ -87,24 +87,24 @@ class UserSessionScreenTests: XCTestCase {
|
|||||||
try await Task.sleep(for: .seconds(1))
|
try await Task.sleep(for: .seconds(1))
|
||||||
try await app.assertScreenshot(step: Step.spaceScreen)
|
try await app.assertScreenshot(step: Step.spaceScreen)
|
||||||
|
|
||||||
app.buttons[A11yIdentifiers.spaceListScreen.spaceRoomName(firstSubspaceName)].tap()
|
app.buttons[A11yIdentifiers.spaceListScreen.spaceRoomName(joinedSubspaceName)].tap()
|
||||||
XCTAssert(app.staticTexts[firstSubspaceName].waitForExistence(timeout: 5.0))
|
XCTAssert(app.staticTexts[joinedSubspaceName].waitForExistence(timeout: 5.0))
|
||||||
try await Task.sleep(for: .seconds(1))
|
try await Task.sleep(for: .seconds(1))
|
||||||
try await app.assertScreenshot(step: Step.subspaceScreen)
|
try await app.assertScreenshot(step: Step.subspaceScreen)
|
||||||
|
|
||||||
app.buttons[A11yIdentifiers.spaceListScreen.spaceRoomName(firstSubspaceRoomName)].tap()
|
app.buttons[A11yIdentifiers.spaceListScreen.spaceRoomName(joinedSubspaceRoomName)].tap()
|
||||||
XCTAssert(app.staticTexts[firstSubspaceRoomName].waitForExistence(timeout: 5.0))
|
XCTAssert(app.staticTexts[joinedSubspaceRoomName].waitForExistence(timeout: 5.0))
|
||||||
try await Task.sleep(for: .seconds(1))
|
try await Task.sleep(for: .seconds(1))
|
||||||
try await app.assertScreenshot(step: Step.subspaceRoomScreen)
|
try await app.assertScreenshot(step: Step.subspaceRoomScreen)
|
||||||
|
|
||||||
app.navigationBars.buttons[firstSubspaceName].firstMatch.tap(.center)
|
app.navigationBars.buttons[joinedSubspaceName].firstMatch.tap(.center)
|
||||||
XCTAssert(app.staticTexts[firstSubspaceName].waitForExistence(timeout: 5.0))
|
XCTAssert(app.staticTexts[joinedSubspaceName].waitForExistence(timeout: 5.0))
|
||||||
|
|
||||||
app.navigationBars.buttons[firstSpaceName].firstMatch.tap(.center)
|
app.navigationBars.buttons[firstSpaceName].firstMatch.tap(.center)
|
||||||
XCTAssert(app.staticTexts[firstSpaceName].waitForExistence(timeout: 5.0))
|
XCTAssert(app.staticTexts[firstSpaceName].waitForExistence(timeout: 5.0))
|
||||||
|
|
||||||
app.buttons[A11yIdentifiers.spaceListScreen.spaceRoomName(firstSpaceRoomName)].tap()
|
app.buttons[A11yIdentifiers.spaceListScreen.spaceRoomName(unjoinedSpaceRoomName)].tap()
|
||||||
XCTAssert(app.staticTexts[firstSpaceRoomName].waitForExistence(timeout: 5.0))
|
XCTAssert(app.staticTexts[unjoinedSpaceRoomName].waitForExistence(timeout: 5.0))
|
||||||
try await Task.sleep(for: .seconds(1))
|
try await Task.sleep(for: .seconds(1))
|
||||||
try await app.assertScreenshot(step: Step.spaceJoinRoomScreen)
|
try await app.assertScreenshot(step: Step.spaceJoinRoomScreen)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ef257a3d7f03a0eaee4e4354c847c90cbc9d0f9d5c52767d1d49d200232337b0
|
oid sha256:411d73e47a4c33cca4ad4a80f234707a11bb2090bd33d081bf0cc4702356b864
|
||||||
size 274101
|
size 277762
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:869be3a8f6ed1705de9fa6d37b6ce0a2e143a6f53a5b439f7cf67e93cf2d082f
|
oid sha256:1f1d0f742cd7ed94b8c20f7b4bb7d2d9aeee77b15118b04f4c14f914db142b62
|
||||||
size 109237
|
size 115249
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class JoinRoomScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
XCTAssertTrue(appSettings.seenInvites.isEmpty, "Only an invited room should register the room ID as a seen invite.")
|
XCTAssertTrue(appSettings.seenInvites.isEmpty, "Only an invited room should register the room ID as a seen invite.")
|
||||||
|
|
||||||
let deferred = deferFulfillment(viewModel.actionsPublisher) { $0 == .joined }
|
let deferred = deferFulfillment(viewModel.actionsPublisher) { $0 == .joined(.roomID("1")) }
|
||||||
context.send(viewAction: .join)
|
context.send(viewAction: .join)
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ class JoinRoomScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
XCTAssertEqual(appSettings.seenInvites, ["1"], "The invited room's ID should be registered as a seen invite.")
|
XCTAssertEqual(appSettings.seenInvites, ["1"], "The invited room's ID should be registered as a seen invite.")
|
||||||
|
|
||||||
let deferred = deferFulfillment(viewModel.actionsPublisher) { $0 == .joined }
|
let deferred = deferFulfillment(viewModel.actionsPublisher) { $0 == .joined(.roomID("1")) }
|
||||||
context.send(viewAction: .acceptInvite)
|
context.send(viewAction: .acceptInvite)
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
@@ -156,6 +156,8 @@ class JoinRoomScreenViewModelTests: XCTestCase {
|
|||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
private func setupViewModel(throwing: Bool = false, mode: TestMode = .joined) {
|
private func setupViewModel(throwing: Bool = false, mode: TestMode = .joined) {
|
||||||
ServiceLocator.shared.settings.knockingEnabled = true
|
ServiceLocator.shared.settings.knockingEnabled = true
|
||||||
|
|
||||||
@@ -199,3 +201,21 @@ class JoinRoomScreenViewModelTests: XCTestCase {
|
|||||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension JoinRoomScreenViewModelAction: @retroactive Equatable {
|
||||||
|
// A close enough approximation for tests.
|
||||||
|
public static func == (lhs: JoinRoomScreenViewModelAction, rhs: JoinRoomScreenViewModelAction) -> Bool {
|
||||||
|
switch (lhs, rhs) {
|
||||||
|
case (.joined(.roomID(let lhsRoomID)), .joined(.roomID(let rhsRoomID))):
|
||||||
|
lhsRoomID == rhsRoomID
|
||||||
|
case (.joined(.space(let lhsSpace)), .joined(.space(let rhsSpace))):
|
||||||
|
lhsSpace.spaceRoomProxy.id == rhsSpace.spaceRoomProxy.id
|
||||||
|
case (.dismiss, .dismiss):
|
||||||
|
true
|
||||||
|
case (.presentDeclineAndBlock(let lhsUserID), .presentDeclineAndBlock(let rhsUserID)):
|
||||||
|
lhsUserID == rhsUserID
|
||||||
|
default:
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -72,7 +72,10 @@ class SpaceListScreenViewModelTests: XCTestCase {
|
|||||||
])
|
])
|
||||||
spaceServiceProxy = SpaceServiceProxyMock(.init())
|
spaceServiceProxy = SpaceServiceProxyMock(.init())
|
||||||
spaceServiceProxy.joinedSpacesPublisher = joinedSpacesSubject.asCurrentValuePublisher()
|
spaceServiceProxy.joinedSpacesPublisher = joinedSpacesSubject.asCurrentValuePublisher()
|
||||||
spaceServiceProxy.spaceRoomListForClosure = { .success(SpaceRoomListProxyMock(.init(spaceRoomProxy: $0))) }
|
spaceServiceProxy.spaceRoomListSpaceIDClosure = { [joinedSpacesSubject] spaceID in
|
||||||
|
guard let spaceRoomProxy = joinedSpacesSubject?.value.first(where: { $0.id == spaceID }) else { return .failure(.missingSpace) }
|
||||||
|
return .success(SpaceRoomListProxyMock(.init(spaceRoomProxy: spaceRoomProxy)))
|
||||||
|
}
|
||||||
clientProxy.spaceService = spaceServiceProxy
|
clientProxy.spaceService = spaceServiceProxy
|
||||||
|
|
||||||
viewModel = SpaceListScreenViewModel(userSession: userSession,
|
viewModel = SpaceListScreenViewModel(userSession: userSession,
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ class SpaceScreenViewModelTests: XCTestCase {
|
|||||||
func testSelectingSpace() async throws {
|
func testSelectingSpace() async throws {
|
||||||
setupViewModel()
|
setupViewModel()
|
||||||
|
|
||||||
let selectedSpace = try XCTUnwrap(mockSpaceRooms.first { $0.isSpace }, "There should be a space to select.")
|
let selectedSpace = try XCTUnwrap(mockSpaceRooms.first { $0.isSpace && $0.state == .joined }, "There should be a space to select.")
|
||||||
let deferred = deferFulfillment(viewModel.actionsPublisher) { _ in true }
|
let deferred = deferFulfillment(viewModel.actionsPublisher) { _ in true }
|
||||||
viewModel.context.send(viewAction: .spaceAction(.select(selectedSpace)))
|
viewModel.context.send(viewAction: .spaceAction(.select(selectedSpace)))
|
||||||
let action = try await deferred.fulfill()
|
let action = try await deferred.fulfill()
|
||||||
@@ -106,6 +106,22 @@ class SpaceScreenViewModelTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testSelectingUnjoinedSpace() async throws {
|
||||||
|
setupViewModel()
|
||||||
|
|
||||||
|
let selectedSpace = try XCTUnwrap(mockSpaceRooms.first { $0.isSpace && $0.state != .joined }, "There should be a space to select.")
|
||||||
|
let deferred = deferFulfillment(viewModel.actionsPublisher) { _ in true }
|
||||||
|
viewModel.context.send(viewAction: .spaceAction(.select(selectedSpace)))
|
||||||
|
let action = try await deferred.fulfill()
|
||||||
|
|
||||||
|
switch action {
|
||||||
|
case .selectUnjoinedSpace(let spaceRoomProxy) where spaceRoomProxy.id == selectedSpace.id:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
XCTFail("The action should select the space.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testSelectingRoom() async throws {
|
func testSelectingRoom() async throws {
|
||||||
setupViewModel()
|
setupViewModel()
|
||||||
|
|
||||||
@@ -125,7 +141,7 @@ class SpaceScreenViewModelTests: XCTestCase {
|
|||||||
func testJoiningSpace() async throws {
|
func testJoiningSpace() async throws {
|
||||||
setupViewModel()
|
setupViewModel()
|
||||||
|
|
||||||
let selectedSpace = try XCTUnwrap(mockSpaceRooms.first { $0.isSpace }, "There should be a space to select.")
|
let selectedSpace = try XCTUnwrap(mockSpaceRooms.first { $0.isSpace && $0.state != .joined }, "There should be a space to select.")
|
||||||
|
|
||||||
let expectation = XCTestExpectation(description: "Join room")
|
let expectation = XCTestExpectation(description: "Join room")
|
||||||
clientProxy.joinRoomViaClosure = { _, _ in
|
clientProxy.joinRoomViaClosure = { _, _ in
|
||||||
@@ -184,7 +200,10 @@ class SpaceScreenViewModelTests: XCTestCase {
|
|||||||
paginationResponses: paginationResponses))
|
paginationResponses: paginationResponses))
|
||||||
|
|
||||||
let spaceServiceProxy = SpaceServiceProxyMock(.init())
|
let spaceServiceProxy = SpaceServiceProxyMock(.init())
|
||||||
spaceServiceProxy.spaceRoomListForClosure = { .success(SpaceRoomListProxyMock(.init(spaceRoomProxy: $0))) }
|
spaceServiceProxy.spaceRoomListSpaceIDClosure = { [mockSpaceRooms] spaceID in
|
||||||
|
guard let spaceRoomProxy = mockSpaceRooms.first(where: { $0.id == spaceID }) else { return .failure(.missingSpace) }
|
||||||
|
return .success(SpaceRoomListProxyMock(.init(spaceRoomProxy: spaceRoomProxy)))
|
||||||
|
}
|
||||||
|
|
||||||
clientProxy = ClientProxyMock(.init())
|
clientProxy = ClientProxyMock(.init())
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user