mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
implemented the flow to create room in a selected space
This commit is contained in:
@@ -67,7 +67,7 @@
|
|||||||
"action_copy_link_to_message" = "Copy link to message";
|
"action_copy_link_to_message" = "Copy link to message";
|
||||||
"action_copy_text" = "Copy text";
|
"action_copy_text" = "Copy text";
|
||||||
"action_create" = "Create";
|
"action_create" = "Create";
|
||||||
"action_create_a_room" = "Create a room";
|
"action_create_a_room" = "Create room";
|
||||||
"action_create_space" = "Create space";
|
"action_create_space" = "Create space";
|
||||||
"action_deactivate" = "Deactivate";
|
"action_deactivate" = "Deactivate";
|
||||||
"action_deactivate_account" = "Deactivate account";
|
"action_deactivate_account" = "Deactivate account";
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
"action_copy_link_to_message" = "Copy link to message";
|
"action_copy_link_to_message" = "Copy link to message";
|
||||||
"action_copy_text" = "Copy text";
|
"action_copy_text" = "Copy text";
|
||||||
"action_create" = "Create";
|
"action_create" = "Create";
|
||||||
"action_create_a_room" = "Create a room";
|
"action_create_a_room" = "Create room";
|
||||||
"action_create_space" = "Create space";
|
"action_create_space" = "Create space";
|
||||||
"action_deactivate" = "Deactivate";
|
"action_deactivate" = "Deactivate";
|
||||||
"action_deactivate_account" = "Deactivate account";
|
"action_deactivate_account" = "Deactivate account";
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
private var membersFlowCoordinator: RoomMembersFlowCoordinator?
|
private var membersFlowCoordinator: RoomMembersFlowCoordinator?
|
||||||
private var settingsFlowCoordinator: SpaceSettingsFlowCoordinator?
|
private var settingsFlowCoordinator: SpaceSettingsFlowCoordinator?
|
||||||
private var rolesAndPermissionsFlowCoordinator: RoomRolesAndPermissionsFlowCoordinator?
|
private var rolesAndPermissionsFlowCoordinator: RoomRolesAndPermissionsFlowCoordinator?
|
||||||
|
private var createChildRoomFlowCoordinator: StartChatFlowCoordinator?
|
||||||
|
|
||||||
indirect enum State: StateType {
|
indirect enum State: StateType {
|
||||||
/// The state machine hasn't started.
|
/// The state machine hasn't started.
|
||||||
@@ -65,6 +66,8 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
case rolesAndPermissionsFlow
|
case rolesAndPermissionsFlow
|
||||||
|
|
||||||
|
case createChildRoomFlow
|
||||||
|
|
||||||
case leftSpace
|
case leftSpace
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +105,9 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
case startRolesAndPermissionsFlow
|
case startRolesAndPermissionsFlow
|
||||||
case stopRolesAndPermissionsFlow
|
case stopRolesAndPermissionsFlow
|
||||||
|
|
||||||
|
case startCreateChildRoomFlow
|
||||||
|
case stopCreateChildRoomFlow
|
||||||
}
|
}
|
||||||
|
|
||||||
private let stateMachine: StateMachine<State, Event>
|
private let stateMachine: StateMachine<State, Event>
|
||||||
@@ -170,12 +176,15 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
case .rolesAndPermissionsFlow:
|
case .rolesAndPermissionsFlow:
|
||||||
rolesAndPermissionsFlowCoordinator?.clearRoute(animated: animated)
|
rolesAndPermissionsFlowCoordinator?.clearRoute(animated: animated)
|
||||||
clearRoute(animated: animated) // Re-run with the state machine back in the .space state.
|
clearRoute(animated: animated) // Re-run with the state machine back in the .space state.
|
||||||
|
case .createChildRoomFlow:
|
||||||
|
createChildRoomFlowCoordinator?.clearRoute(animated: animated)
|
||||||
|
clearRoute(animated: animated) // Re-run with the state machine back in the .space state.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
// swiftlint:disable:next cyclomatic_complexity
|
// swiftlint:disable:next function_body_length cyclomatic_complexity
|
||||||
private func configureStateMachine() {
|
private func configureStateMachine() {
|
||||||
stateMachine.addRoutes(event: .start, transitions: [.initial => .space]) { [weak self] _ in
|
stateMachine.addRoutes(event: .start, transitions: [.initial => .space]) { [weak self] _ in
|
||||||
self?.presentSpace()
|
self?.presentSpace()
|
||||||
@@ -288,6 +297,21 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
rolesAndPermissionsFlowCoordinator = nil
|
rolesAndPermissionsFlowCoordinator = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stateMachine.addRouteMapping { event, fromState, _ in
|
||||||
|
guard event == .startCreateChildRoomFlow, case .space = fromState else { return nil }
|
||||||
|
return .createChildRoomFlow
|
||||||
|
} handler: { [weak self] context in
|
||||||
|
guard let space = context.userInfo as? SpaceServiceRoomProtocol else { fatalError("The space is missing") }
|
||||||
|
self?.startCreateChildFlow(space: space)
|
||||||
|
}
|
||||||
|
|
||||||
|
stateMachine.addRouteMapping { event, fromState, _ in
|
||||||
|
guard event == .stopCreateChildRoomFlow, case .createChildRoomFlow = fromState else { return nil }
|
||||||
|
return .space
|
||||||
|
} handler: { [weak self] _ in
|
||||||
|
self?.createChildRoomFlowCoordinator = nil
|
||||||
|
}
|
||||||
|
|
||||||
stateMachine.addErrorHandler { context in
|
stateMachine.addErrorHandler { context in
|
||||||
fatalError("Unexpected transition: \(context)")
|
fatalError("Unexpected transition: \(context)")
|
||||||
}
|
}
|
||||||
@@ -323,6 +347,8 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
stateMachine.tryEvent(.startRolesAndPermissionsFlow, userInfo: roomProxy)
|
stateMachine.tryEvent(.startRolesAndPermissionsFlow, userInfo: roomProxy)
|
||||||
case .addExistingChildren:
|
case .addExistingChildren:
|
||||||
stateMachine.tryEvent(.addRooms)
|
stateMachine.tryEvent(.addRooms)
|
||||||
|
case .displayCreateChildRoomFlow(let space):
|
||||||
|
stateMachine.tryEvent(.startCreateChildRoomFlow, userInfo: space)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
@@ -534,4 +560,41 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
rolesAndPermissionsFlowCoordinator = flowCoordinator
|
rolesAndPermissionsFlowCoordinator = flowCoordinator
|
||||||
flowCoordinator.start()
|
flowCoordinator.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func startCreateChildFlow(space: SpaceServiceRoomProtocol) {
|
||||||
|
let stackCoordinator = NavigationStackCoordinator()
|
||||||
|
let flowCoordinator = StartChatFlowCoordinator(entryPoint: .createRoomInSpace(space),
|
||||||
|
userDiscoveryService: UserDiscoveryService(clientProxy: flowParameters.userSession.clientProxy),
|
||||||
|
navigationStackCoordinator: stackCoordinator,
|
||||||
|
flowParameters: flowParameters)
|
||||||
|
|
||||||
|
var flowCoordinatorResult: StartChatFlowCoordinatorAction.Result?
|
||||||
|
flowCoordinator.actionsPublisher.sink { [weak self] action in
|
||||||
|
guard let self else { return }
|
||||||
|
switch action {
|
||||||
|
case .finished(let result):
|
||||||
|
flowCoordinatorResult = result
|
||||||
|
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
|
case .showRoomDirectory:
|
||||||
|
fatalError("Not implemented yet")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
navigationStackCoordinator.setSheetCoordinator(stackCoordinator) { [weak self] in
|
||||||
|
guard let self else { return }
|
||||||
|
stateMachine.tryEvent(.stopCreateChildRoomFlow)
|
||||||
|
switch flowCoordinatorResult {
|
||||||
|
case .room(let id):
|
||||||
|
stateMachine.tryEvent(.startRoomFlow(roomID: id))
|
||||||
|
case .space(let spaceRoomListProxy):
|
||||||
|
stateMachine.tryEvent(.startChildFlow, userInfo: spaceRoomListProxy)
|
||||||
|
case .cancelled, .none:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createChildRoomFlowCoordinator = flowCoordinator
|
||||||
|
flowCoordinator.start()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ class SpacesTabFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
navigationSplitCoordinator.setSheetCoordinator(coordinator) { [weak self] in
|
navigationSplitCoordinator.setSheetCoordinator(coordinator) { [weak self] in
|
||||||
self?.stateMachine.tryEvent(.dismissedCreateSpaceFlow, userInfo: spaceRoomListProxy)
|
self?.stateMachine.tryEvent(.dismissedCreateSpaceFlow)
|
||||||
}
|
}
|
||||||
|
|
||||||
flowCoordinator.start(animated: true)
|
flowCoordinator.start(animated: true)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ enum StartChatFlowCoordinatorAction {
|
|||||||
enum StartChatFlowCoordinatorEntryPoint {
|
enum StartChatFlowCoordinatorEntryPoint {
|
||||||
case startChat
|
case startChat
|
||||||
case createSpace
|
case createSpace
|
||||||
|
case createRoomInSpace(SpaceServiceRoomProtocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
||||||
@@ -57,7 +58,7 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
enum Event: EventType {
|
enum Event: EventType {
|
||||||
/// The flow is being started.
|
/// The flow is being started.
|
||||||
case start(entryPoint: StartChatFlowCoordinatorEntryPoint)
|
case start
|
||||||
|
|
||||||
/// The user would like to create a room.
|
/// The user would like to create a room.
|
||||||
case createRoom
|
case createRoom
|
||||||
@@ -96,7 +97,7 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func start(animated: Bool) {
|
func start(animated: Bool) {
|
||||||
stateMachine.tryEvent(.start(entryPoint: entryPoint))
|
stateMachine.tryEvent(.start)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
||||||
@@ -126,12 +127,24 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func configureStateMachine() {
|
private func configureStateMachine() {
|
||||||
stateMachine.addRoutes(event: .start(entryPoint: .startChat), transitions: [.initial => .startChat]) { [weak self] _ in
|
stateMachine.addRouteMapping { [weak self] event, fromState, _ in
|
||||||
self?.presentStartChatScreen()
|
guard let self, event == .start, fromState == .initial else { return nil }
|
||||||
}
|
switch entryPoint {
|
||||||
|
case .startChat:
|
||||||
stateMachine.addRoutes(event: .start(entryPoint: .createSpace), transitions: [.initial => .createRoom]) { [weak self] _ in
|
return .startChat
|
||||||
self?.presentCreateRoomScreen(isSpace: true, isRoot: true)
|
case .createSpace, .createRoomInSpace:
|
||||||
|
return .createRoom
|
||||||
|
}
|
||||||
|
} handler: { [weak self] _ in
|
||||||
|
guard let self else { return }
|
||||||
|
switch entryPoint {
|
||||||
|
case .startChat:
|
||||||
|
presentStartChatScreen()
|
||||||
|
case .createSpace:
|
||||||
|
presentCreateRoomScreen(isSpace: true, isRoot: true)
|
||||||
|
case .createRoomInSpace(let space):
|
||||||
|
presentCreateRoomScreen(isSpace: false, selectedSpace: space, isRoot: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stateMachine.addRoutes(event: .createRoom, transitions: [.startChat => .createRoom]) { [weak self] _ in
|
stateMachine.addRoutes(event: .createRoom, transitions: [.startChat => .createRoom]) { [weak self] _ in
|
||||||
@@ -191,9 +204,17 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func presentCreateRoomScreen(isSpace: Bool, isRoot: Bool) {
|
private func presentCreateRoomScreen(isSpace: Bool, selectedSpace: SpaceServiceRoomProtocol? = nil, isRoot: Bool) {
|
||||||
|
let spaceSelectionMode: CreateRoomScreenSpaceSelectionMode? = if let selectedSpace {
|
||||||
|
.selected(selectedSpace)
|
||||||
|
} else if !isSpace, flowParameters.appSettings.createSpaceEnabled {
|
||||||
|
.list
|
||||||
|
} else {
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
|
||||||
let createParameters = CreateRoomScreenCoordinatorParameters(isSpace: isSpace,
|
let createParameters = CreateRoomScreenCoordinatorParameters(isSpace: isSpace,
|
||||||
spaceSelectionMode: !isSpace && flowParameters.appSettings.createSpaceEnabled ? .list : nil,
|
spaceSelectionMode: spaceSelectionMode,
|
||||||
shouldShowCancelButton: isRoot,
|
shouldShowCancelButton: isRoot,
|
||||||
userSession: flowParameters.userSession,
|
userSession: flowParameters.userSession,
|
||||||
userIndicatorController: flowParameters.userIndicatorController,
|
userIndicatorController: flowParameters.userIndicatorController,
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ internal enum L10n {
|
|||||||
internal static var actionCopyText: String { return L10n.tr("Localizable", "action_copy_text") }
|
internal static var actionCopyText: String { return L10n.tr("Localizable", "action_copy_text") }
|
||||||
/// Create
|
/// Create
|
||||||
internal static var actionCreate: String { return L10n.tr("Localizable", "action_create") }
|
internal static var actionCreate: String { return L10n.tr("Localizable", "action_create") }
|
||||||
/// Create a room
|
/// Create room
|
||||||
internal static var actionCreateARoom: String { return L10n.tr("Localizable", "action_create_a_room") }
|
internal static var actionCreateARoom: String { return L10n.tr("Localizable", "action_create_a_room") }
|
||||||
/// Create space
|
/// Create space
|
||||||
internal static var actionCreateSpace: String { return L10n.tr("Localizable", "action_create_space") }
|
internal static var actionCreateSpace: String { return L10n.tr("Localizable", "action_create_space") }
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ enum SpaceScreenCoordinatorAction {
|
|||||||
case displaySpaceSettings(roomProxy: JoinedRoomProxyProtocol)
|
case displaySpaceSettings(roomProxy: JoinedRoomProxyProtocol)
|
||||||
case displayRolesAndPermissions(roomProxy: JoinedRoomProxyProtocol)
|
case displayRolesAndPermissions(roomProxy: JoinedRoomProxyProtocol)
|
||||||
case addExistingChildren
|
case addExistingChildren
|
||||||
|
case displayCreateChildRoomFlow(space: SpaceServiceRoomProtocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class SpaceScreenCoordinator: CoordinatorProtocol {
|
final class SpaceScreenCoordinator: CoordinatorProtocol {
|
||||||
@@ -75,6 +76,8 @@ final class SpaceScreenCoordinator: CoordinatorProtocol {
|
|||||||
actionsSubject.send(.displayRolesAndPermissions(roomProxy: roomProxy))
|
actionsSubject.send(.displayRolesAndPermissions(roomProxy: roomProxy))
|
||||||
case .addExistingChildren:
|
case .addExistingChildren:
|
||||||
actionsSubject.send(.addExistingChildren)
|
actionsSubject.send(.addExistingChildren)
|
||||||
|
case .displayCreateChildRoomFlow(let space):
|
||||||
|
actionsSubject.send(.displayCreateChildRoomFlow(space: space))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ enum SpaceScreenViewModelAction {
|
|||||||
case displayMembers(roomProxy: JoinedRoomProxyProtocol)
|
case displayMembers(roomProxy: JoinedRoomProxyProtocol)
|
||||||
case displaySpaceSettings(roomProxy: JoinedRoomProxyProtocol)
|
case displaySpaceSettings(roomProxy: JoinedRoomProxyProtocol)
|
||||||
case addExistingChildren
|
case addExistingChildren
|
||||||
|
case displayCreateChildRoomFlow(space: SpaceServiceRoomProtocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SpaceScreenViewState: BindableState {
|
struct SpaceScreenViewState: BindableState {
|
||||||
@@ -34,6 +35,7 @@ struct SpaceScreenViewState: BindableState {
|
|||||||
var canEditRolesAndPermissions = false
|
var canEditRolesAndPermissions = false
|
||||||
var canEditSecurityAndPrivacy = false
|
var canEditSecurityAndPrivacy = false
|
||||||
var canEditChildren = false
|
var canEditChildren = false
|
||||||
|
var canCreateRoom = false
|
||||||
|
|
||||||
var editMode: EditMode = .inactive
|
var editMode: EditMode = .inactive
|
||||||
var editModeSelectedIDs: Set<String> = []
|
var editModeSelectedIDs: Set<String> = []
|
||||||
@@ -72,6 +74,7 @@ enum SpaceScreenViewAction {
|
|||||||
case spaceSettings(roomProxy: JoinedRoomProxyProtocol)
|
case spaceSettings(roomProxy: JoinedRoomProxyProtocol)
|
||||||
case displayMembers(roomProxy: JoinedRoomProxyProtocol)
|
case displayMembers(roomProxy: JoinedRoomProxyProtocol)
|
||||||
case addExistingRooms
|
case addExistingRooms
|
||||||
|
case createChildRoom
|
||||||
case manageChildren
|
case manageChildren
|
||||||
case removeSelectedChildren
|
case removeSelectedChildren
|
||||||
case confirmRemoveSelectedChildren
|
case confirmRemoveSelectedChildren
|
||||||
|
|||||||
@@ -75,6 +75,10 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
|
|||||||
.weakAssign(to: \.state.selectedSpaceRoomID, on: self)
|
.weakAssign(to: \.state.selectedSpaceRoomID, on: self)
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
appSettings.$createSpaceEnabled
|
||||||
|
.weakAssign(to: \.state.canCreateRoom, on: self)
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
if case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(spaceRoomListProxy.id) {
|
if case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(spaceRoomListProxy.id) {
|
||||||
// Required to listen for membership updates in the members flow
|
// Required to listen for membership updates in the members flow
|
||||||
@@ -155,6 +159,8 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
|
|||||||
} completion: {
|
} completion: {
|
||||||
self.state.editModeSelectedIDs.removeAll()
|
self.state.editModeSelectedIDs.removeAll()
|
||||||
}
|
}
|
||||||
|
case .createChildRoom:
|
||||||
|
Task { await createChildRoom() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,6 +171,16 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
|
|||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func createChildRoom() async {
|
||||||
|
switch await spaceServiceProxy.spaceForIdentifier(spaceID: spaceRoomListProxy.id) {
|
||||||
|
case .success(.some(let space)):
|
||||||
|
actionsSubject.send(.displayCreateChildRoomFlow(space: space))
|
||||||
|
default:
|
||||||
|
MXLog.error("Unable to create child room: space not found")
|
||||||
|
userIndicatorController.submitIndicator(.init(title: L10n.errorUnknown))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func join(_ spaceServiceRoom: SpaceServiceRoomProtocol) async {
|
private func join(_ spaceServiceRoom: SpaceServiceRoomProtocol) async {
|
||||||
state.joiningRoomIDs.insert(spaceServiceRoom.id)
|
state.joiningRoomIDs.insert(spaceServiceRoom.id)
|
||||||
defer { state.joiningRoomIDs.remove(spaceServiceRoom.id) }
|
defer { state.joiningRoomIDs.remove(spaceServiceRoom.id) }
|
||||||
|
|||||||
@@ -121,6 +121,11 @@ struct SpaceScreen: View {
|
|||||||
Menu {
|
Menu {
|
||||||
if context.viewState.canEditChildren {
|
if context.viewState.canEditChildren {
|
||||||
Section {
|
Section {
|
||||||
|
if context.viewState.canCreateRoom {
|
||||||
|
Button { context.send(viewAction: .createChildRoom) } label: {
|
||||||
|
Label(L10n.actionCreateARoom, icon: \.plus)
|
||||||
|
}
|
||||||
|
}
|
||||||
Button { context.send(viewAction: .addExistingRooms) } label: {
|
Button { context.send(viewAction: .addExistingRooms) } label: {
|
||||||
Label(L10n.actionAddExistingRooms, icon: \.room)
|
Label(L10n.actionAddExistingRooms, icon: \.room)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user