Create space flow (#4957)
* create space flow implementation # Conflicts: # ElementX/Sources/FlowCoordinators/SpacesTabFlowCoordinator.swift * create space flow fully implemented and working * updated tests and updated the create room camera button UI * updated the avatar button in the create room screen, and added power level overrides for spaces * update power level content overrides to behave just as EW, and removed ask to join when creating a space regardless of the FF * updated UI tests snapshots * invite for a public space should always be forced to 50 * pr suggestions + PL override fix * fix a missed code change
This commit is contained in:
@@ -567,7 +567,7 @@ class ChatsTabFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
private func startStartChatFlow(animated: Bool) {
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let coordinator = StartChatFlowCoordinator(isSpace: false,
|
||||
let coordinator = StartChatFlowCoordinator(entryPoint: .startChat,
|
||||
userDiscoveryService: UserDiscoveryService(clientProxy: userSession.clientProxy),
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
flowParameters: flowParameters)
|
||||
@@ -576,11 +576,17 @@ class ChatsTabFlowCoordinator: FlowCoordinatorProtocol {
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .finished(let roomID):
|
||||
case .finished(let result):
|
||||
navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
|
||||
if let roomID {
|
||||
|
||||
switch result {
|
||||
case .room(let roomID):
|
||||
stateMachine.processEvent(.selectRoom(roomID: roomID, via: [], entryPoint: .room))
|
||||
case .space(let spaceRoomListProxy):
|
||||
// This also automatically handles selecting the space.
|
||||
stateMachine.processEvent(.selectRoom(roomID: spaceRoomListProxy.id, via: [], entryPoint: .room))
|
||||
case .cancelled:
|
||||
break
|
||||
}
|
||||
case .showRoomDirectory:
|
||||
navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
|
||||
@@ -25,12 +25,15 @@ class SpacesTabFlowCoordinator: FlowCoordinatorProtocol {
|
||||
private let detailNavigationStackCoordinator: NavigationStackCoordinator
|
||||
|
||||
private var spaceFlowCoordinator: SpaceFlowCoordinator?
|
||||
private var startChatFlowCoordinator: StartChatFlowCoordinator?
|
||||
|
||||
enum State: StateType {
|
||||
/// The state machine hasn't started.
|
||||
case initial
|
||||
/// The root screen for this flow.
|
||||
case spacesScreen(selectedSpaceID: String?)
|
||||
/// The create space flow is currently being presented
|
||||
case createSpaceFlow
|
||||
}
|
||||
|
||||
enum Event: EventType {
|
||||
@@ -42,6 +45,10 @@ class SpacesTabFlowCoordinator: FlowCoordinatorProtocol {
|
||||
case selectSpace
|
||||
/// The space screen has been dismissed.
|
||||
case deselectSpace
|
||||
/// Start the create a new space flow
|
||||
case startCreateSpaceFlow
|
||||
/// Create space has finished
|
||||
case dismissedCreateSpaceFlow
|
||||
}
|
||||
|
||||
private let stateMachine: StateMachine<State, Event>
|
||||
@@ -81,16 +88,20 @@ class SpacesTabFlowCoordinator: FlowCoordinatorProtocol {
|
||||
switch stateMachine.state {
|
||||
case .initial, .spacesScreen:
|
||||
break
|
||||
case .createSpaceFlow:
|
||||
startChatFlowCoordinator?.clearRoute(animated: animated)
|
||||
clearRoute(animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
// swiftlint:disable:next cyclomatic_complexity
|
||||
private func configureStateMachine() {
|
||||
stateMachine.addRoutes(event: .start, transitions: [.initial => .spacesScreen(selectedSpaceID: nil)]) { [weak self] _ in
|
||||
self?.presentSpacesScreen()
|
||||
}
|
||||
|
||||
|
||||
stateMachine.addRouteMapping { event, fromState, userInfo in
|
||||
guard event == .selectSpace, case .spacesScreen = fromState else { return nil }
|
||||
guard let spaceRoomListProxy = userInfo as? SpaceRoomListProxyProtocol else { fatalError("A space proxy must be provided.") }
|
||||
@@ -110,6 +121,24 @@ class SpacesTabFlowCoordinator: FlowCoordinatorProtocol {
|
||||
spaceFlowCoordinator = nil
|
||||
}
|
||||
|
||||
stateMachine.addRouteMapping { event, fromState, _ in
|
||||
guard event == .startCreateSpaceFlow, case .spacesScreen = fromState else { return nil }
|
||||
return .createSpaceFlow
|
||||
} handler: { [weak self] _ in
|
||||
self?.startCreateSpaceFlow()
|
||||
}
|
||||
|
||||
stateMachine.addRouteMapping { event, fromState, userInfo in
|
||||
guard event == .dismissedCreateSpaceFlow, case .createSpaceFlow = fromState else { return nil }
|
||||
return .spacesScreen(selectedSpaceID: (userInfo as? SpaceRoomListProxyProtocol)?.id)
|
||||
} handler: { [weak self] context in
|
||||
guard let self else { return }
|
||||
startChatFlowCoordinator = nil
|
||||
if let spaceRoomListProxy = context.userInfo as? SpaceRoomListProxyProtocol {
|
||||
startSpaceFlow(spaceRoomListProxy: spaceRoomListProxy)
|
||||
}
|
||||
}
|
||||
|
||||
stateMachine.addErrorHandler { context in
|
||||
fatalError("Unexpected transition: \(context)")
|
||||
}
|
||||
@@ -129,6 +158,8 @@ class SpacesTabFlowCoordinator: FlowCoordinatorProtocol {
|
||||
stateMachine.tryEvent(.selectSpace, userInfo: spaceRoomListProxy)
|
||||
case .showSettings:
|
||||
actionsSubject.send(.showSettings)
|
||||
case .showCreateSpace:
|
||||
stateMachine.tryEvent(.startCreateSpaceFlow)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
@@ -167,4 +198,38 @@ class SpacesTabFlowCoordinator: FlowCoordinatorProtocol {
|
||||
coordinator.start()
|
||||
selectedSpaceSubject.send(spaceRoomListProxy.id)
|
||||
}
|
||||
|
||||
private func startCreateSpaceFlow() {
|
||||
let coordinator = NavigationStackCoordinator()
|
||||
let flowCoordinator = StartChatFlowCoordinator(entryPoint: .createSpace,
|
||||
userDiscoveryService: UserDiscoveryService(clientProxy: flowParameters.userSession.clientProxy),
|
||||
navigationStackCoordinator: coordinator,
|
||||
flowParameters: flowParameters)
|
||||
|
||||
var spaceRoomListProxy: SpaceRoomListProxyProtocol?
|
||||
flowCoordinator.actionsPublisher
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .finished(let result):
|
||||
switch result {
|
||||
case .space(let value):
|
||||
spaceRoomListProxy = value
|
||||
case .room, .cancelled:
|
||||
break
|
||||
}
|
||||
navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
case .showRoomDirectory:
|
||||
fatalError("Not handled here")
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
navigationSplitCoordinator.setSheetCoordinator(coordinator) { [weak self] in
|
||||
self?.stateMachine.tryEvent(.dismissedCreateSpaceFlow, userInfo: spaceRoomListProxy)
|
||||
}
|
||||
|
||||
flowCoordinator.start(animated: true)
|
||||
startChatFlowCoordinator = flowCoordinator
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,29 @@ import Foundation
|
||||
import SwiftState
|
||||
|
||||
enum StartChatFlowCoordinatorAction {
|
||||
case finished(roomID: String?)
|
||||
case finished(Result)
|
||||
case showRoomDirectory
|
||||
|
||||
enum Result {
|
||||
case room(id: String)
|
||||
case space(spaceRoomListProxy: SpaceRoomListProxyProtocol)
|
||||
case cancelled
|
||||
}
|
||||
}
|
||||
|
||||
/// A value that represents where the flow will be started.
|
||||
enum StartChatFlowCoordinatorEntryPoint {
|
||||
case startChat
|
||||
case createSpace
|
||||
}
|
||||
|
||||
class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
||||
private let isSpace: Bool
|
||||
struct CreatedRoomResult {
|
||||
let roomProxy: JoinedRoomProxyProtocol
|
||||
let spaceRoomListProxy: SpaceRoomListProxyProtocol?
|
||||
}
|
||||
|
||||
private let entryPoint: StartChatFlowCoordinatorEntryPoint
|
||||
private let userDiscoveryService: UserDiscoveryServiceProtocol
|
||||
private let navigationStackCoordinator: NavigationStackCoordinator
|
||||
|
||||
@@ -40,7 +57,7 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
enum Event: EventType {
|
||||
/// The flow is being started.
|
||||
case start
|
||||
case start(entryPoint: StartChatFlowCoordinatorEntryPoint)
|
||||
|
||||
/// The user would like to create a room.
|
||||
case createRoom
|
||||
@@ -64,11 +81,11 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(isSpace: Bool,
|
||||
init(entryPoint: StartChatFlowCoordinatorEntryPoint,
|
||||
userDiscoveryService: UserDiscoveryServiceProtocol,
|
||||
navigationStackCoordinator: NavigationStackCoordinator,
|
||||
flowParameters: CommonFlowParameters) {
|
||||
self.isSpace = isSpace
|
||||
self.entryPoint = entryPoint
|
||||
self.userDiscoveryService = userDiscoveryService
|
||||
self.navigationStackCoordinator = navigationStackCoordinator
|
||||
|
||||
@@ -79,7 +96,7 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
||||
}
|
||||
|
||||
func start(animated: Bool) {
|
||||
stateMachine.tryEvent(.start)
|
||||
stateMachine.tryEvent(.start(entryPoint: entryPoint))
|
||||
}
|
||||
|
||||
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
||||
@@ -109,12 +126,16 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
||||
// MARK: - Private
|
||||
|
||||
private func configureStateMachine() {
|
||||
stateMachine.addRoutes(event: .start, transitions: [.initial => .startChat]) { [weak self] _ in
|
||||
stateMachine.addRoutes(event: .start(entryPoint: .startChat), transitions: [.initial => .startChat]) { [weak self] _ in
|
||||
self?.presentStartChatScreen()
|
||||
}
|
||||
|
||||
stateMachine.addRoutes(event: .start(entryPoint: .createSpace), transitions: [.initial => .createRoom]) { [weak self] _ in
|
||||
self?.presentCreateRoomScreen(isSpace: true, isRoot: true)
|
||||
}
|
||||
|
||||
stateMachine.addRoutes(event: .createRoom, transitions: [.startChat => .createRoom]) { [weak self] _ in
|
||||
self?.presentCreateRoomScreen()
|
||||
self?.presentCreateRoomScreen(isSpace: false, isRoot: false)
|
||||
}
|
||||
stateMachine.addRoutes(event: .dismissedCreateRoom, transitions: [.createRoom => .startChat]) { [weak self] _ in
|
||||
self?.createRoomScreenCoordinator = nil
|
||||
@@ -129,10 +150,10 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
||||
stateMachine.addRoutes(event: .dismissedRoomAvatarPicker, transitions: [.roomAvatarPicker => .createRoom])
|
||||
|
||||
stateMachine.addRoutes(event: .createdRoom, transitions: [.createRoom => .inviteUsers]) { [weak self] context in
|
||||
guard let roomProxy = context.userInfo as? JoinedRoomProxyProtocol else {
|
||||
guard let result = context.userInfo as? CreatedRoomResult else {
|
||||
fatalError("A room proxy is required to invite users.")
|
||||
}
|
||||
self?.presentInviteUsersScreen(roomProxy: roomProxy)
|
||||
self?.presentInviteUsersScreen(roomProxy: result.roomProxy, spaceRoomListProxy: result.spaceRoomListProxy)
|
||||
}
|
||||
|
||||
stateMachine.addErrorHandler { context in
|
||||
@@ -156,11 +177,11 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .close:
|
||||
actionsSubject.send(.finished(roomID: nil))
|
||||
actionsSubject.send(.finished(.cancelled))
|
||||
case .createRoom:
|
||||
stateMachine.tryEvent(.createRoom)
|
||||
case .openRoom(let roomID):
|
||||
actionsSubject.send(.finished(roomID: roomID))
|
||||
actionsSubject.send(.finished(.room(id: roomID)))
|
||||
case .openRoomDirectorySearch:
|
||||
actionsSubject.send(.showRoomDirectory)
|
||||
}
|
||||
@@ -170,8 +191,9 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
}
|
||||
|
||||
private func presentCreateRoomScreen() {
|
||||
private func presentCreateRoomScreen(isSpace: Bool, isRoot: Bool) {
|
||||
let createParameters = CreateRoomScreenCoordinatorParameters(isSpace: isSpace,
|
||||
shouldShowCancelButton: isRoot,
|
||||
userSession: flowParameters.userSession,
|
||||
userIndicatorController: flowParameters.userIndicatorController,
|
||||
appSettings: flowParameters.appSettings,
|
||||
@@ -180,17 +202,25 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .createdRoom(let roomProxy):
|
||||
stateMachine.tryEvent(.createdRoom, userInfo: roomProxy)
|
||||
case .createdRoom(let roomProxy, let spaceRoomListProxy):
|
||||
stateMachine.tryEvent(.createdRoom, userInfo: CreatedRoomResult(roomProxy: roomProxy, spaceRoomListProxy: spaceRoomListProxy))
|
||||
case .displayMediaPickerWithMode(let mode):
|
||||
stateMachine.tryEvent(.presentRoomAvatarPicker, userInfo: mode)
|
||||
case .dismiss:
|
||||
// Only used when isRoot
|
||||
actionsSubject.send(.finished(.cancelled))
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
createRoomScreenCoordinator = coordinator
|
||||
navigationStackCoordinator.push(coordinator) { [weak self] in
|
||||
self?.stateMachine.tryEvent(.dismissedCreateRoom)
|
||||
|
||||
if isRoot {
|
||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
} else {
|
||||
navigationStackCoordinator.push(coordinator) { [weak self] in
|
||||
self?.stateMachine.tryEvent(.dismissedCreateRoom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,7 +249,7 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
private func presentInviteUsersScreen(roomProxy: JoinedRoomProxyProtocol) {
|
||||
private func presentInviteUsersScreen(roomProxy: JoinedRoomProxyProtocol, spaceRoomListProxy: SpaceRoomListProxyProtocol?) {
|
||||
let inviteParameters = InviteUsersScreenCoordinatorParameters(userSession: flowParameters.userSession,
|
||||
roomProxy: roomProxy,
|
||||
isSkippable: true,
|
||||
@@ -231,7 +261,11 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .dismiss:
|
||||
actionsSubject.send(.finished(roomID: roomProxy.id))
|
||||
if let spaceRoomListProxy {
|
||||
actionsSubject.send(.finished(.space(spaceRoomListProxy: spaceRoomListProxy)))
|
||||
} else {
|
||||
actionsSubject.send(.finished(.room(id: roomProxy.id)))
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
@@ -10,18 +10,17 @@ import SwiftUI
|
||||
|
||||
struct ToolbarButton: View {
|
||||
enum Role {
|
||||
case cancel
|
||||
case done
|
||||
case save
|
||||
static let cancel = Role.cancel(title: L10n.actionCancel)
|
||||
static let done = Role.confirm(title: L10n.actionDone)
|
||||
static let save = Role.confirm(title: L10n.actionSave)
|
||||
|
||||
case cancel(title: String)
|
||||
case confirm(title: String)
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .cancel:
|
||||
L10n.actionCancel
|
||||
case .done:
|
||||
L10n.actionDone
|
||||
case .save:
|
||||
L10n.actionSave
|
||||
case .cancel(let title), .confirm(let title):
|
||||
title
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +30,7 @@ struct ToolbarButton: View {
|
||||
case .cancel:
|
||||
CompoundIcon(\.close)
|
||||
.foregroundStyle(.compound.iconPrimary)
|
||||
case .done, .save:
|
||||
case .confirm:
|
||||
CompoundIcon(\.check)
|
||||
.foregroundStyle(.compound.iconOnSolidPrimary)
|
||||
}
|
||||
@@ -41,7 +40,7 @@ struct ToolbarButton: View {
|
||||
switch self {
|
||||
case .cancel:
|
||||
.compound.bgCanvasDefault
|
||||
case .done, .save:
|
||||
case .confirm:
|
||||
.compound.bgAccentRest
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import SwiftUI
|
||||
|
||||
struct CreateRoomScreenCoordinatorParameters {
|
||||
let isSpace: Bool
|
||||
let shouldShowCancelButton: Bool
|
||||
let userSession: UserSessionProtocol
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
let appSettings: AppSettings
|
||||
@@ -18,8 +19,9 @@ struct CreateRoomScreenCoordinatorParameters {
|
||||
}
|
||||
|
||||
enum CreateRoomScreenCoordinatorAction {
|
||||
case createdRoom(JoinedRoomProxyProtocol)
|
||||
case createdRoom(JoinedRoomProxyProtocol, SpaceRoomListProxyProtocol?)
|
||||
case displayMediaPickerWithMode(MediaPickerScreenMode)
|
||||
case dismiss
|
||||
}
|
||||
|
||||
final class CreateRoomScreenCoordinator: CoordinatorProtocol {
|
||||
@@ -33,6 +35,7 @@ final class CreateRoomScreenCoordinator: CoordinatorProtocol {
|
||||
|
||||
init(parameters: CreateRoomScreenCoordinatorParameters) {
|
||||
viewModel = CreateRoomScreenViewModel(isSpace: parameters.isSpace,
|
||||
shouldShowCancelButton: parameters.shouldShowCancelButton,
|
||||
userSession: parameters.userSession,
|
||||
analytics: parameters.analytics,
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
@@ -43,12 +46,14 @@ final class CreateRoomScreenCoordinator: CoordinatorProtocol {
|
||||
viewModel.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .createdRoom(let roomProxy):
|
||||
actionsSubject.send(.createdRoom(roomProxy))
|
||||
case .createdRoom(let roomProxy, let spaceRoomListProxy):
|
||||
actionsSubject.send(.createdRoom(roomProxy, spaceRoomListProxy))
|
||||
case .displayCameraPicker:
|
||||
actionsSubject.send(.displayMediaPickerWithMode(.init(source: .camera, selectionType: .single)))
|
||||
case .displayMediaPicker:
|
||||
actionsSubject.send(.displayMediaPickerWithMode(.init(source: .photoLibrary, selectionType: .single)))
|
||||
case .dismiss:
|
||||
actionsSubject.send(.dismiss)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
@@ -17,13 +17,15 @@ enum CreateRoomScreenErrorType: Error {
|
||||
}
|
||||
|
||||
enum CreateRoomScreenViewModelAction {
|
||||
case createdRoom(JoinedRoomProxyProtocol)
|
||||
case createdRoom(JoinedRoomProxyProtocol, SpaceRoomListProxyProtocol?)
|
||||
case displayMediaPicker
|
||||
case displayCameraPicker
|
||||
case dismiss
|
||||
}
|
||||
|
||||
struct CreateRoomScreenViewState: BindableState {
|
||||
let isSpace: Bool
|
||||
let shouldShowCancelButton: Bool
|
||||
var roomName: String
|
||||
let serverName: String
|
||||
let isKnockingFeatureEnabled: Bool
|
||||
@@ -47,7 +49,7 @@ struct CreateRoomScreenViewState: BindableState {
|
||||
|
||||
var availableAccessTypes: [CreateRoomAccessType] {
|
||||
var availableTypes = CreateRoomAccessType.allCases
|
||||
if !isKnockingFeatureEnabled {
|
||||
if isSpace || !isKnockingFeatureEnabled {
|
||||
availableTypes.removeAll { $0 == .askToJoin }
|
||||
}
|
||||
return availableTypes
|
||||
@@ -64,6 +66,7 @@ struct CreateRoomScreenViewStateBindings {
|
||||
}
|
||||
|
||||
enum CreateRoomScreenViewAction {
|
||||
case dismiss
|
||||
case createRoom
|
||||
case displayCameraPicker
|
||||
case displayMediaPicker
|
||||
|
||||
@@ -27,6 +27,7 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
|
||||
}
|
||||
|
||||
init(isSpace: Bool,
|
||||
shouldShowCancelButton: Bool,
|
||||
userSession: UserSessionProtocol,
|
||||
analytics: AnalyticsService,
|
||||
userIndicatorController: UserIndicatorControllerProtocol,
|
||||
@@ -40,6 +41,7 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
|
||||
selectedAccessType: .private)
|
||||
|
||||
super.init(initialViewState: CreateRoomScreenViewState(isSpace: isSpace,
|
||||
shouldShowCancelButton: shouldShowCancelButton,
|
||||
roomName: "",
|
||||
serverName: userSession.clientProxy.userIDServerName ?? "",
|
||||
isKnockingFeatureEnabled: appSettings.knockingEnabled,
|
||||
@@ -54,6 +56,8 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
|
||||
|
||||
override func process(viewAction: CreateRoomScreenViewAction) {
|
||||
switch viewAction {
|
||||
case .dismiss:
|
||||
actionsSubject.send(.dismiss)
|
||||
case .createRoom:
|
||||
Task { await createRoom() }
|
||||
case .displayCameraPicker:
|
||||
@@ -233,7 +237,19 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
|
||||
return
|
||||
}
|
||||
analytics.trackCreatedRoom(isDM: false)
|
||||
actionsSubject.send(.createdRoom(roomProxy))
|
||||
|
||||
var spaceRoomListProxy: SpaceRoomListProxyProtocol?
|
||||
if state.isSpace {
|
||||
switch await userSession.clientProxy.spaceService.spaceRoomList(spaceID: roomProxy.id) {
|
||||
case .success(let value):
|
||||
spaceRoomListProxy = value
|
||||
case .failure:
|
||||
MXLog.error("Failed to get space room list for newly created space with id: \(roomProxy.id)")
|
||||
userIndicatorController.submitIndicator(.init(title: L10n.errorUnknown))
|
||||
}
|
||||
}
|
||||
|
||||
actionsSubject.send(.createdRoom(roomProxy, spaceRoomListProxy))
|
||||
case .failure:
|
||||
state.bindings.alertInfo = AlertInfo(id: .failedCreatingRoom,
|
||||
title: L10n.commonError,
|
||||
|
||||
@@ -54,8 +54,18 @@ struct CreateRoomScreen: View {
|
||||
.shouldScrollOnKeyboardDidShow(focus == .alias, to: Focus.alias)
|
||||
}
|
||||
|
||||
private var nameTextFieldShape: AnyShape {
|
||||
if #available(iOS 26, *) {
|
||||
AnyShape(ConcentricRectangle(corners: .concentric(minimum: 26)))
|
||||
} else {
|
||||
AnyShape(RoundedRectangle(cornerRadius: 12))
|
||||
}
|
||||
}
|
||||
|
||||
private var roomSection: some View {
|
||||
Section {
|
||||
EmptyView()
|
||||
} header: {
|
||||
HStack(alignment: .center, spacing: 16) {
|
||||
roomAvatarButton
|
||||
let nameLabel = if #available(iOS 26, *) {
|
||||
@@ -80,11 +90,11 @@ struct CreateRoomScreen: View {
|
||||
.accessibilityIdentifier(A11yIdentifiers.createRoomScreen.roomName)
|
||||
.padding(.horizontal, ListRowPadding.horizontal)
|
||||
.padding(.vertical, ListRowPadding.vertical)
|
||||
.background(.compound.bgCanvasDefaultLevel1, in: RoundedRectangle(cornerRadius: 12))
|
||||
.background(.compound.bgCanvasDefaultLevel1, in: nameTextFieldShape)
|
||||
}
|
||||
}
|
||||
.listRowInsets(.init())
|
||||
.listRowBackground(Color.clear)
|
||||
.padding(.top, 16)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,15 +111,27 @@ struct CreateRoomScreen: View {
|
||||
} placeholder: {
|
||||
ProgressView()
|
||||
}
|
||||
.scaledFrame(size: 70)
|
||||
.clipShape(Circle())
|
||||
.scaledFrame(size: 70, relativeTo: .title)
|
||||
.clipShape(context.viewState.isSpace ? AnyShape(RoundedRectangle(cornerRadius: 16)) : AnyShape(Circle()))
|
||||
.overlay(alignment: .bottomTrailing) {
|
||||
editAvatarBadge
|
||||
.scaledOffset(x: 12, y: 4, relativeTo: .title)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
} else {
|
||||
CompoundIcon(\.takePhoto, size: .custom(36), relativeTo: .title)
|
||||
.foregroundColor(.compound.iconSecondary)
|
||||
.scaledFrame(size: 70, relativeTo: .title)
|
||||
.background(.compound.bgSubtlePrimary, in: Circle())
|
||||
CompoundIcon(\.takePhoto, size: .medium, relativeTo: .title)
|
||||
.foregroundColor(.compound.iconPrimary)
|
||||
.scaledFrame(size: 50, relativeTo: .title)
|
||||
.background(.compound.bgCanvasDefault, in: Circle())
|
||||
.overlay {
|
||||
Circle()
|
||||
.stroke(.compound.borderInteractiveSecondary, lineWidth: 1)
|
||||
}
|
||||
.padding(10)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
}
|
||||
.accessibilityLabel(L10n.a11yEditAvatar)
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityIdentifier(A11yIdentifiers.createRoomScreen.roomAvatar)
|
||||
.confirmationDialog("", isPresented: $context.showAttachmentConfirmationDialog) {
|
||||
@@ -129,6 +151,23 @@ struct CreateRoomScreen: View {
|
||||
}
|
||||
}
|
||||
|
||||
private var editAvatarBadge: some View {
|
||||
CompoundIcon(\.edit, size: .small, relativeTo: .body)
|
||||
.foregroundStyle(.compound.iconPrimary)
|
||||
.scaledPadding(5, relativeTo: .title)
|
||||
.background {
|
||||
Circle()
|
||||
.fill(Color.compound.bgCanvasDefault)
|
||||
.overlay {
|
||||
Circle()
|
||||
.inset(by: 0.5)
|
||||
.stroke(.compound.borderInteractiveSecondary, lineWidth: 1)
|
||||
}
|
||||
}
|
||||
.scaledPadding(3.5, relativeTo: .title)
|
||||
.background(.compound.bgSubtleSecondaryLevel0, in: Circle())
|
||||
}
|
||||
|
||||
private var topicSection: some View {
|
||||
Section {
|
||||
ListRow(label: .plain(title: L10n.screenCreateRoomTopicPlaceholder),
|
||||
@@ -180,9 +219,18 @@ struct CreateRoomScreen: View {
|
||||
}
|
||||
}
|
||||
|
||||
@ToolbarContentBuilder
|
||||
private var toolbar: some ToolbarContent {
|
||||
if context.viewState.shouldShowCancelButton {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
ToolbarButton(role: .cancel) {
|
||||
context.send(viewAction: .dismiss)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button(L10n.actionCreate) {
|
||||
ToolbarButton(role: .confirm(title: L10n.actionCreate)) {
|
||||
focus = nil
|
||||
context.send(viewAction: .createRoom)
|
||||
}
|
||||
@@ -247,26 +295,55 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
||||
AppSettings.resetAllSettings()
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com"))))
|
||||
return CreateRoomScreenViewModel(isSpace: false,
|
||||
shouldShowCancelButton: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}()
|
||||
|
||||
static let avatarViewModel = {
|
||||
AppSettings.resetAllSettings()
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com"))))
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: false,
|
||||
shouldShowCancelButton: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
viewModel.updateAvatar(fileURL: Bundle.main.url(forResource: "preview_avatar_room", withExtension: "jpg")!)
|
||||
return viewModel
|
||||
}()
|
||||
|
||||
static let spaceViewModel = {
|
||||
AppSettings.resetAllSettings()
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com"))))
|
||||
return CreateRoomScreenViewModel(isSpace: true,
|
||||
shouldShowCancelButton: true,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}()
|
||||
|
||||
static let spaceWithAvatarViewModel = {
|
||||
AppSettings.resetAllSettings()
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com"))))
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: true,
|
||||
shouldShowCancelButton: true,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
viewModel.updateAvatar(fileURL: Bundle.main.url(forResource: "preview_avatar_room", withExtension: "jpg")!)
|
||||
return viewModel
|
||||
}()
|
||||
|
||||
static let publicRoomViewModel = {
|
||||
AppSettings.resetAllSettings()
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com"))))
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: false,
|
||||
shouldShowCancelButton: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
@@ -281,6 +358,7 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
||||
appSettings.knockingEnabled = true
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com"))))
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: false,
|
||||
shouldShowCancelButton: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
@@ -293,6 +371,7 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
||||
AppSettings.resetAllSettings()
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com"))))
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: false,
|
||||
shouldShowCancelButton: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
@@ -308,6 +387,7 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
||||
clientProxy.isAliasAvailableReturnValue = .success(false)
|
||||
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: false,
|
||||
shouldShowCancelButton: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
@@ -323,11 +403,23 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
||||
}
|
||||
.previewDisplayName("Create Room")
|
||||
|
||||
NavigationStack {
|
||||
CreateRoomScreen(context: avatarViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Create Room with avatar")
|
||||
.snapshotPreferences(expect: avatarViewModel.context.$viewState.map { $0.avatarMediaInfo != nil })
|
||||
|
||||
NavigationStack {
|
||||
CreateRoomScreen(context: spaceViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Create Space")
|
||||
|
||||
NavigationStack {
|
||||
CreateRoomScreen(context: spaceWithAvatarViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Create Space with avatar")
|
||||
.snapshotPreferences(expect: spaceWithAvatarViewModel.context.$viewState.map { $0.avatarMediaInfo != nil })
|
||||
|
||||
NavigationStack {
|
||||
CreateRoomScreen(context: publicRoomViewModel.context)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ struct SpacesScreenCoordinatorParameters {
|
||||
enum SpacesScreenCoordinatorAction {
|
||||
case selectSpace(SpaceRoomListProxyProtocol)
|
||||
case showSettings
|
||||
case showCreateSpace
|
||||
}
|
||||
|
||||
final class SpacesScreenCoordinator: CoordinatorProtocol {
|
||||
@@ -51,6 +52,8 @@ final class SpacesScreenCoordinator: CoordinatorProtocol {
|
||||
actionsSubject.send(.selectSpace(spaceRoomListProxy))
|
||||
case .showSettings:
|
||||
actionsSubject.send(.showSettings)
|
||||
case .showCreateSpace:
|
||||
actionsSubject.send(.showCreateSpace)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
@@ -11,6 +11,7 @@ import Foundation
|
||||
enum SpacesScreenViewModelAction {
|
||||
case selectSpace(SpaceRoomListProxyProtocol)
|
||||
case showSettings
|
||||
case showCreateSpace
|
||||
}
|
||||
|
||||
struct SpacesScreenViewState: BindableState {
|
||||
|
||||
@@ -79,8 +79,7 @@ class SpacesScreenViewModel: SpacesScreenViewModelType, SpacesScreenViewModelPro
|
||||
case .featureAnnouncementAppeared:
|
||||
appSettings.hasSeenSpacesAnnouncement = true
|
||||
case .createSpace:
|
||||
// TODO: Implement
|
||||
break
|
||||
actionsSubject.send(.showCreateSpace)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ class ClientProxy: ClientProxyProtocol {
|
||||
ban: nil,
|
||||
kick: nil,
|
||||
redact: nil,
|
||||
invite: nil,
|
||||
invite: Int32(0),
|
||||
notifications: nil,
|
||||
users: [:],
|
||||
events: [
|
||||
@@ -91,6 +91,32 @@ class ClientProxy: ClientProxyProtocol {
|
||||
"org.matrix.msc3401.call.member": Int32(0)
|
||||
])
|
||||
}
|
||||
|
||||
private static var standardSpaceCreationPowerLevelOverrides: PowerLevels {
|
||||
.init(usersDefault: nil,
|
||||
eventsDefault: Int32(100),
|
||||
stateDefault: nil,
|
||||
ban: nil,
|
||||
kick: nil,
|
||||
redact: nil,
|
||||
invite: Int32(50),
|
||||
notifications: nil,
|
||||
users: [:],
|
||||
events: [:])
|
||||
}
|
||||
|
||||
private static var publicSpaceCreationPowerLevelOverrides: PowerLevels {
|
||||
.init(usersDefault: nil,
|
||||
eventsDefault: Int32(100),
|
||||
stateDefault: nil,
|
||||
ban: nil,
|
||||
kick: nil,
|
||||
redact: nil,
|
||||
invite: Int32(0),
|
||||
notifications: nil,
|
||||
users: [:],
|
||||
events: [:])
|
||||
}
|
||||
|
||||
private var loadCachedAvatarURLTask: Task<Void, Never>?
|
||||
private let userAvatarURLSubject = CurrentValueSubject<URL?, Never>(nil)
|
||||
@@ -480,6 +506,20 @@ class ClientProxy: ClientProxyProtocol {
|
||||
avatarURL: URL?,
|
||||
aliasLocalPart: String?) async -> Result<String, ClientProxyError> {
|
||||
do {
|
||||
let powerLevelContentOverride = if isSpace {
|
||||
if accessType == .public {
|
||||
Self.publicSpaceCreationPowerLevelOverrides
|
||||
} else {
|
||||
Self.standardSpaceCreationPowerLevelOverrides
|
||||
}
|
||||
} else {
|
||||
if accessType == .askToJoin {
|
||||
Self.knockingRoomCreationPowerLevelOverrides
|
||||
} else {
|
||||
Self.roomCreationPowerLevelOverrides
|
||||
}
|
||||
}
|
||||
|
||||
let parameters = CreateRoomParameters(name: name,
|
||||
topic: topic,
|
||||
isEncrypted: accessType.isEncrypted,
|
||||
@@ -488,7 +528,7 @@ class ClientProxy: ClientProxyProtocol {
|
||||
preset: accessType.preset,
|
||||
invite: userIDs,
|
||||
avatar: avatarURL?.absoluteString,
|
||||
powerLevelContentOverride: accessType == .askToJoin ? Self.knockingRoomCreationPowerLevelOverrides : Self.roomCreationPowerLevelOverrides,
|
||||
powerLevelContentOverride: powerLevelContentOverride,
|
||||
joinRuleOverride: accessType.joinRuleOverride,
|
||||
historyVisibilityOverride: accessType.historyVisibilityOverride,
|
||||
// This is an FFI naming mistake, what is required is the `aliasLocalPart` not the whole alias
|
||||
|
||||
@@ -659,7 +659,7 @@ class MockScreen: Identifiable {
|
||||
userDiscoveryService.searchProfilesWithReturnValue = .success([.mockBob, .mockBobby])
|
||||
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let flowCoordinator = StartChatFlowCoordinator(isSpace: false,
|
||||
let flowCoordinator = StartChatFlowCoordinator(entryPoint: .startChat,
|
||||
userDiscoveryService: userDiscoveryService,
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
flowParameters: CommonFlowParameters(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f513aac79644e109b9cd0be2cf88e47362a70bdd39e92688fecbeab9d49e6414
|
||||
size 157858
|
||||
oid sha256:aa327c6149879d4aa96fa4c5574bac7486fc6abfaa003ae74596c8b88adf6b0b
|
||||
size 156228
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f4dcf0f103278fa02af6c0d97f54791e14f25d1f5ec0e31eba5b080e21ce6858
|
||||
size 187550
|
||||
oid sha256:704b115de7bad6d64ea253ea261a8812e536622dc32280a11e91a3bdc9473340
|
||||
size 186268
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e6e0f7572a47ad8fbc35bbe862e8d0634dd1357f4559bd54d6cb57d481485763
|
||||
size 105733
|
||||
oid sha256:66f4d91f70ce033d8c6553479ecf7471d53ffdb181986cea4c9032701a595c25
|
||||
size 106657
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:abcffb042721e323327f3c70f4cdca89ff56654887293b2f9b2215cc486d465c
|
||||
size 142332
|
||||
oid sha256:0c355007d6ac8f669a701f0ef534e6e381895722d3c495be9e4ef49d20a7de79
|
||||
size 143489
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c744f0ddc6f0e2cb2e2063cc0135d18a07f18d3a58314ee33651962295199225
|
||||
size 158006
|
||||
oid sha256:31efa5cc91a3b0a623056e85e8d5e0c6544a464d959b29ef771e2c9fa2843d7e
|
||||
size 156446
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:301081a176b706092f008e01c9a195dd58c7dc28adc7a42d38ee5e4c168cd1a2
|
||||
size 189421
|
||||
oid sha256:e638ad86f90e438978096a9fc928f85b6ca0d883f02221978307fe3068e20e35
|
||||
size 188066
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7128f0417b2479525d2b35bfb4a501ac24690694f34841bc7ecf0d922741654d
|
||||
size 109846
|
||||
oid sha256:9b51dfc640af59dd5b3564148d48a1267e9423b1c77fb02edc4607d91d10910b
|
||||
size 108742
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2a656ea4051f6a84282fca5f94978b199ec10df51dac04a7ad01c4ec55b6ae1f
|
||||
size 146993
|
||||
oid sha256:8b8b277cc422ac3f6fba5b7b555e42c18d2b6ecde65a36eba6c4b63279fb80b5
|
||||
size 145650
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2c110232459827a0f641057ed1bfc6bfdb714134f3a658fa5acee9a9395bf62a
|
||||
size 142457
|
||||
oid sha256:b71055f825775861294542ae166fbe7013800fc3672ce4d39ab4419716eedf45
|
||||
size 141129
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:729a6515bc93eb02be4d04a60c07f3854875ab2404a282a5de8bd11ae9e86a89
|
||||
size 165595
|
||||
oid sha256:d2b85ce0989c5e068e489de22275aeaf723e51fe6b18cb304d16b831e8e8f368
|
||||
size 164236
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9d4b216831eb0595f1b3adbf0fade0d3db2e621d7922dbc647b62fc31ddc854f
|
||||
size 88801
|
||||
oid sha256:a5adc15a61b998447a66a4696b59f89413381583c4455be02b1f28594701b986
|
||||
size 89299
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c3cfa55a1a89d1905c5c21d5aeadce37d8554487054c2abe6c7d39323e7359c4
|
||||
size 115037
|
||||
oid sha256:5823312ac25afe1b349f8eda8807299d6c30656e961d387c01e544eda40e6c4e
|
||||
size 116112
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ea061eb260fdeebd3d38a6f32c209345f45dfe3188bff5ebb4e5fefa26bd73e3
|
||||
size 161310
|
||||
oid sha256:400c0c0ed48ced1122851b21a6d3e9e48f0985635711fec7e4abe8660994a6b1
|
||||
size 159870
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:cc8f86bff3d1377c3ca9cca775c998a736a90211d7bb4d03439aa788bdc60160
|
||||
size 197756
|
||||
oid sha256:575f32a8b119f2ffbaf6e234b9ea23366850ea245f3f63403a9ba0a2691a2a3b
|
||||
size 195889
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c58d9126bdf00d7b64ac59b740645d456ad06fe66ff67f0e805eb4b140f0adc1
|
||||
size 111712
|
||||
oid sha256:9002a2ad841596a8e206326fa282b131b750cc556e57a8b8fbf355318106e6ac
|
||||
size 110759
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3cc77ead9f512f919377e076cf95cba2678a0eb3d7c79395dfcdd33a82d32d3f
|
||||
size 152160
|
||||
oid sha256:fab82b9e91cb4b3bad0a3bd13e1b2077a992473889ddb62297d0c902d724e104
|
||||
size 150819
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:26726c399334387ad4f4f9603af729dd998d8aa40e31175def2b1719e3fe27ec
|
||||
size 124557
|
||||
oid sha256:9db61c6bde46ef08cfd7d1aceb50f379544053024ac4633d1234c863c98c066a
|
||||
size 123222
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6f0b7534290fc773b98633250f05b434ef83c5f8243dd891de5bc58902941e28
|
||||
size 142357
|
||||
oid sha256:e287da07572fd2bd3424894537844637e57629235b372108a8d99ae1b0994b1b
|
||||
size 140788
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:37a460ee15e802c3a165053bba730a911518682570d0692c2f1228913936cb95
|
||||
size 72654
|
||||
oid sha256:8b820693bf00b97aaa0a797808106267ce741e495bba57936de8082dc4745bbf
|
||||
size 73369
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8b1447dc8d94468cbb42821eea9bc4431ad6c26682a3ef3901d11d80bddaed9a
|
||||
size 89114
|
||||
oid sha256:ff2841d40e79e09cc472837c7ff350082d6d60894539302056c0d20f645264b1
|
||||
size 90112
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9540a4055953e7e3462ccff5605af75f435f5ce7743f09f745d560c3daba7afd
|
||||
size 122057
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d573231fbd9098847e02e294a362116e6b71a5959b37cfcd46043dfc18e5d2de
|
||||
size 139671
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:eebf3dac12738f6f864f382ac9b136e29c19ae9c9874d900891a1c55c6d006da
|
||||
size 72559
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:68000903e55e1ba5a98b082b33b65d03c111c5751947bf6297f1a4fd46d0189f
|
||||
size 89331
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0152d34aba8e7c97fd38b48433a83dee21ceaea96aacbb070782d65a75bfc4ce
|
||||
size 125037
|
||||
oid sha256:b112313be37d156c4ec72d85ec9a2859d36dda4d0674171d9dea25a637eabc47
|
||||
size 124039
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5e9b590ea56f6498031583f2eceda6c1269a697b532006d8226a5c0fb152ca41
|
||||
size 142802
|
||||
oid sha256:029da760106a69b208e4cb55df1a78d6552bc8a6bed70a31e1c1efbf6bf60721
|
||||
size 141624
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c672bfb3589592b0cdcd373ce02e47ebcdaf695d8f1e009381f2c527f326aed2
|
||||
size 73155
|
||||
oid sha256:a069f80fe0ac2f396ac06f5b508fee9f5cec7880ecbc047dbb351ffab5132ef2
|
||||
size 73872
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b61ca92955968e5905967c5a62da3de062a561f608d6da3f7e200776a76ebfda
|
||||
size 89771
|
||||
oid sha256:2e46256ac2ff573d13b1428bfdc5f06c536b9ab263812fa52c3e0a0ec89a6993
|
||||
size 90692
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:619174a05f46847dfcab7d8edb7687ac87a6691ba2e7cbbb7965fd7d607269ff
|
||||
size 122895
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:957faa7854136c335661ece9b9b57fa40ecee742cbaf2f01b8e96035f267a859
|
||||
size 140534
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c7b4bd8e3da8f046b783b20fd2ac3f13fb6dd9fab1db692e5fa57f6a46f25f14
|
||||
size 73322
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a87553939add3b0a99da570833fed8c0607afa8a10b0543c16d08154ddba34ff
|
||||
size 90212
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:43381daca1607ec68fc02e2d1287c9e3323fc8c44b168cb585a1378cc29724e3
|
||||
size 188246
|
||||
oid sha256:7058d76a8ad923e53494351880460242e1da254cabac48785b858eed3048a3f8
|
||||
size 174195
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6288614b6c59539ad70d8e8beb7c733fa5c4d5f4bf2b630eefb8394dd5cbc4f1
|
||||
size 185149
|
||||
oid sha256:fc69f92a9394c36a6e56d80af159821a1ef7a601c9361b24534db43d8cbae987
|
||||
size 172308
|
||||
|
||||
Binary file not shown.
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a60bf270de7142d5712b239a6f1c1c80ccb9b67312b06e624509441816c5c3a2
|
||||
size 159489
|
||||
oid sha256:2c44caac7d1ea4a4539d36e5b28a6649d1ca4d9e6c34c0c83558a3518e9428a4
|
||||
size 134111
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9136ad78475665ced621f4fbc0d3c751d68995c4a860496cacf55a6eb6c5bb16
|
||||
size 154668
|
||||
oid sha256:d94d00822ddcddeda53539e81d9b2a2c01100fb573e28cfae36aa2ea87d6fe86
|
||||
size 132023
|
||||
|
||||
Binary file not shown.
@@ -29,6 +29,7 @@ class CreateRoomScreenViewModelTests: XCTestCase {
|
||||
userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
ServiceLocator.shared.settings.knockingEnabled = true
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: false,
|
||||
shouldShowCancelButton: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
@@ -38,6 +39,9 @@ class CreateRoomScreenViewModelTests: XCTestCase {
|
||||
|
||||
override func tearDown() {
|
||||
AppSettings.resetAllSettings()
|
||||
viewModel = nil
|
||||
clientProxy = nil
|
||||
userSession = nil
|
||||
}
|
||||
|
||||
func testDefaultSecurity() {
|
||||
@@ -60,7 +64,46 @@ class CreateRoomScreenViewModelTests: XCTestCase {
|
||||
// When creating the room.
|
||||
clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReturnValue = .success("1")
|
||||
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||
guard case .createdRoom(let roomProxy) = action, roomProxy.id == "1" else { return false }
|
||||
guard case .createdRoom(let roomProxy, nil) = action, roomProxy.id == "1" else { return false }
|
||||
return true
|
||||
}
|
||||
context.send(viewAction: .createRoom)
|
||||
try await deferred.fulfill()
|
||||
|
||||
// Then the room should be created and a topic should not be set.
|
||||
XCTAssertTrue(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartCalled)
|
||||
XCTAssertEqual(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.name, "A")
|
||||
XCTAssertNil(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.topic,
|
||||
"The topic should be sent as nil when it is empty.")
|
||||
}
|
||||
|
||||
func testCreateSpace() async throws {
|
||||
clientProxy = ClientProxyMock(.init(userIDServerName: "matrix.org",
|
||||
userID: "@a:b.com",
|
||||
spaceServiceConfiguration: .init(spaceRoomLists: ["1": .init()])))
|
||||
clientProxy.roomForIdentifierClosure = { roomID in .joined(JoinedRoomProxyMock(.init(id: roomID))) }
|
||||
userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
ServiceLocator.shared.settings.knockingEnabled = true
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: true,
|
||||
shouldShowCancelButton: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
self.viewModel = viewModel
|
||||
|
||||
// Given a form with a blank topic.
|
||||
context.send(viewAction: .updateRoomName("A"))
|
||||
context.roomTopic = ""
|
||||
context.selectedAccessType = .private
|
||||
XCTAssertTrue(context.viewState.canCreateRoom)
|
||||
|
||||
// When creating the room.
|
||||
clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReturnValue = .success("1")
|
||||
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||
guard case .createdRoom(let roomProxy, let spaceRoomListProxy) = action,
|
||||
spaceRoomListProxy != nil,
|
||||
roomProxy.id == "1" else { return false }
|
||||
return true
|
||||
}
|
||||
context.send(viewAction: .createRoom)
|
||||
|
||||
Reference in New Issue
Block a user