mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 15:45:11 +00:00
Create room redesign and refactor (#4942)
This commit is contained in:
@@ -567,7 +567,8 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
private func startStartChatFlow(animated: Bool) {
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let coordinator = StartChatFlowCoordinator(userDiscoveryService: UserDiscoveryService(clientProxy: userSession.clientProxy),
|
||||
let coordinator = StartChatFlowCoordinator(isSpace: false,
|
||||
userDiscoveryService: UserDiscoveryService(clientProxy: userSession.clientProxy),
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
flowParameters: flowParameters)
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ enum StartChatFlowCoordinatorAction {
|
||||
}
|
||||
|
||||
class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
||||
private let isSpace: Bool
|
||||
private let userDiscoveryService: UserDiscoveryServiceProtocol
|
||||
private let navigationStackCoordinator: NavigationStackCoordinator
|
||||
|
||||
@@ -63,9 +64,11 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(userDiscoveryService: UserDiscoveryServiceProtocol,
|
||||
init(isSpace: Bool,
|
||||
userDiscoveryService: UserDiscoveryServiceProtocol,
|
||||
navigationStackCoordinator: NavigationStackCoordinator,
|
||||
flowParameters: CommonFlowParameters) {
|
||||
self.isSpace = isSpace
|
||||
self.userDiscoveryService = userDiscoveryService
|
||||
self.navigationStackCoordinator = navigationStackCoordinator
|
||||
|
||||
@@ -168,7 +171,8 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
|
||||
}
|
||||
|
||||
private func presentCreateRoomScreen() {
|
||||
let createParameters = CreateRoomScreenCoordinatorParameters(userSession: flowParameters.userSession,
|
||||
let createParameters = CreateRoomScreenCoordinatorParameters(isSpace: isSpace,
|
||||
userSession: flowParameters.userSession,
|
||||
userIndicatorController: flowParameters.userIndicatorController,
|
||||
appSettings: flowParameters.appSettings,
|
||||
analytics: flowParameters.analytics)
|
||||
|
||||
@@ -1571,6 +1571,10 @@ internal enum L10n {
|
||||
internal static var screenCreateRoomErrorCreatingRoom: String { return L10n.tr("Localizable", "screen_create_room_error_creating_room") }
|
||||
/// The space could not be created because of an unknown error. Try again later.
|
||||
internal static var screenCreateRoomErrorCreatingSpace: String { return L10n.tr("Localizable", "screen_create_room_error_creating_space") }
|
||||
/// Add name…
|
||||
internal static var screenCreateRoomNamePlaceholder: String { return L10n.tr("Localizable", "screen_create_room_name_placeholder") }
|
||||
/// New room
|
||||
internal static var screenCreateRoomNewRoomTitle: String { return L10n.tr("Localizable", "screen_create_room_new_room_title") }
|
||||
/// New space
|
||||
internal static var screenCreateRoomNewSpaceTitle: String { return L10n.tr("Localizable", "screen_create_room_new_space_title") }
|
||||
/// Only people invited can join.
|
||||
@@ -1584,32 +1588,30 @@ internal enum L10n {
|
||||
internal static var screenCreateRoomPublicOptionShortDescription: String { return L10n.tr("Localizable", "screen_create_room_public_option_short_description") }
|
||||
/// Public
|
||||
internal static var screenCreateRoomPublicOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_public_option_title") }
|
||||
/// Anyone can join.
|
||||
internal static var screenCreateRoomRoomAccessSectionAnyoneOptionDescription: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_anyone_option_description") }
|
||||
/// Anyone
|
||||
internal static var screenCreateRoomRoomAccessSectionAnyoneOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_anyone_option_title") }
|
||||
/// Room Access
|
||||
internal static var screenCreateRoomRoomAccessSectionHeader: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_header") }
|
||||
/// Anyone can ask to join but an administrator or a moderator must accept the request.
|
||||
internal static var screenCreateRoomRoomAccessSectionKnockingOptionDescription: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_knocking_option_description") }
|
||||
/// Ask to join
|
||||
/// Allow ask to join
|
||||
internal static var screenCreateRoomRoomAccessSectionKnockingOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_knocking_option_title") }
|
||||
/// Only people invited can join.
|
||||
internal static var screenCreateRoomRoomAccessSectionPrivateOptionDescription: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_private_option_description") }
|
||||
/// Private
|
||||
internal static var screenCreateRoomRoomAccessSectionPrivateOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_private_option_title") }
|
||||
/// Anyone can join.
|
||||
internal static var screenCreateRoomRoomAccessSectionPublicOptionDescription: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_public_option_description") }
|
||||
/// Public
|
||||
internal static var screenCreateRoomRoomAccessSectionPublicOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_public_option_title") }
|
||||
/// Who has access
|
||||
internal static var screenCreateRoomRoomAccessSectionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_title") }
|
||||
/// In order for this room to be visible in the public room directory, you will need a room address.
|
||||
/// You’ll need an address in order to make it visible in the public directory.
|
||||
internal static var screenCreateRoomRoomAddressSectionFooter: String { return L10n.tr("Localizable", "screen_create_room_room_address_section_footer") }
|
||||
/// Room address
|
||||
/// Address
|
||||
internal static var screenCreateRoomRoomAddressSectionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_address_section_title") }
|
||||
/// Room name
|
||||
internal static var screenCreateRoomRoomNameLabel: String { return L10n.tr("Localizable", "screen_create_room_room_name_label") }
|
||||
/// Room visibility
|
||||
internal static var screenCreateRoomRoomVisibilitySectionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_visibility_section_title") }
|
||||
/// Create a room
|
||||
internal static var screenCreateRoomTitle: String { return L10n.tr("Localizable", "screen_create_room_title") }
|
||||
/// New space
|
||||
internal static var screenCreateRoomTitleNewSpace: String { return L10n.tr("Localizable", "screen_create_room_title_new_space") }
|
||||
/// Topic (optional)
|
||||
internal static var screenCreateRoomTopicLabel: String { return L10n.tr("Localizable", "screen_create_room_topic_label") }
|
||||
/// Add description…
|
||||
internal static var screenCreateRoomTopicPlaceholder: String { return L10n.tr("Localizable", "screen_create_room_topic_placeholder") }
|
||||
/// Please confirm that you want to deactivate your account. This action cannot be undone.
|
||||
internal static var screenDeactivateAccountConfirmationDialogContent: String { return L10n.tr("Localizable", "screen_deactivate_account_confirmation_dialog_content") }
|
||||
/// Delete all my messages
|
||||
|
||||
@@ -73,7 +73,7 @@ extension ClientProxyMock {
|
||||
canDeactivateAccount = false
|
||||
directRoomForUserIDReturnValue = .failure(.sdkError(ClientProxyMockError.generic))
|
||||
createDirectRoomWithExpectedRoomNameReturnValue = .failure(.sdkError(ClientProxyMockError.generic))
|
||||
createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReturnValue = .failure(.sdkError(ClientProxyMockError.generic))
|
||||
createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReturnValue = .failure(.sdkError(ClientProxyMockError.generic))
|
||||
canJoinRoomWithReturnValue = true
|
||||
joinRoomViaClosure = { roomID, _ in
|
||||
configuration.overrides.joinedRoomIDs.insert(roomID)
|
||||
|
||||
@@ -2847,15 +2847,15 @@ class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
|
||||
}
|
||||
//MARK: - createRoom
|
||||
|
||||
var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount = 0
|
||||
var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartCallsCount: Int {
|
||||
var createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount = 0
|
||||
var createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount
|
||||
return createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount
|
||||
returnValue = createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
@@ -2863,29 +2863,29 @@ class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount = newValue
|
||||
createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount = newValue
|
||||
createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartCalled: Bool {
|
||||
return createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartCallsCount > 0
|
||||
var createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartCalled: Bool {
|
||||
return createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartCallsCount > 0
|
||||
}
|
||||
var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReceivedArguments: (name: String, topic: String?, isRoomPrivate: Bool, isKnockingOnly: Bool, userIDs: [String], avatarURL: URL?, aliasLocalPart: String?)?
|
||||
var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReceivedInvocations: [(name: String, topic: String?, isRoomPrivate: Bool, isKnockingOnly: Bool, userIDs: [String], avatarURL: URL?, aliasLocalPart: String?)] = []
|
||||
var createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments: (name: String, topic: String?, accessType: CreateRoomAccessType, isSpace: Bool, userIDs: [String], avatarURL: URL?, aliasLocalPart: String?)?
|
||||
var createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedInvocations: [(name: String, topic: String?, accessType: CreateRoomAccessType, isSpace: Bool, userIDs: [String], avatarURL: URL?, aliasLocalPart: String?)] = []
|
||||
|
||||
var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingReturnValue: Result<String, ClientProxyError>!
|
||||
var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReturnValue: Result<String, ClientProxyError>! {
|
||||
var createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingReturnValue: Result<String, ClientProxyError>!
|
||||
var createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReturnValue: Result<String, ClientProxyError>! {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingReturnValue
|
||||
return createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingReturnValue
|
||||
} else {
|
||||
var returnValue: Result<String, ClientProxyError>? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingReturnValue
|
||||
returnValue = createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingReturnValue
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
@@ -2893,26 +2893,26 @@ class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingReturnValue = newValue
|
||||
createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingReturnValue = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingReturnValue = newValue
|
||||
createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingReturnValue = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartClosure: ((String, String?, Bool, Bool, [String], URL?, String?) async -> Result<String, ClientProxyError>)?
|
||||
var createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartClosure: ((String, String?, CreateRoomAccessType, Bool, [String], URL?, String?) async -> Result<String, ClientProxyError>)?
|
||||
|
||||
func createRoom(name: String, topic: String?, isRoomPrivate: Bool, isKnockingOnly: Bool, userIDs: [String], avatarURL: URL?, aliasLocalPart: String?) async -> Result<String, ClientProxyError> {
|
||||
createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartCallsCount += 1
|
||||
createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReceivedArguments = (name: name, topic: topic, isRoomPrivate: isRoomPrivate, isKnockingOnly: isKnockingOnly, userIDs: userIDs, avatarURL: avatarURL, aliasLocalPart: aliasLocalPart)
|
||||
func createRoom(name: String, topic: String?, accessType: CreateRoomAccessType, isSpace: Bool, userIDs: [String], avatarURL: URL?, aliasLocalPart: String?) async -> Result<String, ClientProxyError> {
|
||||
createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartCallsCount += 1
|
||||
createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments = (name: name, topic: topic, accessType: accessType, isSpace: isSpace, userIDs: userIDs, avatarURL: avatarURL, aliasLocalPart: aliasLocalPart)
|
||||
DispatchQueue.main.async {
|
||||
self.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReceivedInvocations.append((name: name, topic: topic, isRoomPrivate: isRoomPrivate, isKnockingOnly: isKnockingOnly, userIDs: userIDs, avatarURL: avatarURL, aliasLocalPart: aliasLocalPart))
|
||||
self.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedInvocations.append((name: name, topic: topic, accessType: accessType, isSpace: isSpace, userIDs: userIDs, avatarURL: avatarURL, aliasLocalPart: aliasLocalPart))
|
||||
}
|
||||
if let createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartClosure = createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartClosure {
|
||||
return await createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartClosure(name, topic, isRoomPrivate, isKnockingOnly, userIDs, avatarURL, aliasLocalPart)
|
||||
if let createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartClosure = createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartClosure {
|
||||
return await createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartClosure(name, topic, accessType, isSpace, userIDs, avatarURL, aliasLocalPart)
|
||||
} else {
|
||||
return createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReturnValue
|
||||
return createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - joinRoom
|
||||
|
||||
@@ -10,6 +10,7 @@ import Combine
|
||||
import SwiftUI
|
||||
|
||||
struct CreateRoomScreenCoordinatorParameters {
|
||||
let isSpace: Bool
|
||||
let userSession: UserSessionProtocol
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
let appSettings: AppSettings
|
||||
@@ -31,7 +32,8 @@ final class CreateRoomScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
|
||||
init(parameters: CreateRoomScreenCoordinatorParameters) {
|
||||
viewModel = CreateRoomScreenViewModel(userSession: parameters.userSession,
|
||||
viewModel = CreateRoomScreenViewModel(isSpace: parameters.isSpace,
|
||||
userSession: parameters.userSession,
|
||||
analytics: parameters.analytics,
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
appSettings: parameters.appSettings)
|
||||
|
||||
@@ -23,12 +23,13 @@ enum CreateRoomScreenViewModelAction {
|
||||
}
|
||||
|
||||
struct CreateRoomScreenViewState: BindableState {
|
||||
let isSpace: Bool
|
||||
var roomName: String
|
||||
let serverName: String
|
||||
let isKnockingFeatureEnabled: Bool
|
||||
var aliasLocalPart: String
|
||||
var bindings: CreateRoomScreenViewStateBindings
|
||||
var avatarURL: URL?
|
||||
var avatarMediaInfo: MediaInfo?
|
||||
var canCreateRoom: Bool {
|
||||
!roomName.isEmpty && aliasErrors.isEmpty
|
||||
}
|
||||
@@ -43,12 +44,19 @@ struct CreateRoomScreenViewState: BindableState {
|
||||
nil
|
||||
}
|
||||
}
|
||||
|
||||
var availableAccessTypes: [CreateRoomAccessType] {
|
||||
var availableTypes = CreateRoomAccessType.allCases
|
||||
if !isKnockingFeatureEnabled {
|
||||
availableTypes.removeAll { $0 == .askToJoin }
|
||||
}
|
||||
return availableTypes
|
||||
}
|
||||
}
|
||||
|
||||
struct CreateRoomScreenViewStateBindings {
|
||||
var roomTopic: String
|
||||
var isRoomPrivate: Bool
|
||||
var isKnockingOnly: Bool
|
||||
var selectedAccessType: CreateRoomAccessType
|
||||
var showAttachmentConfirmationDialog = false
|
||||
|
||||
/// Information describing the currently displayed alert.
|
||||
|
||||
@@ -13,17 +13,7 @@ import SwiftUI
|
||||
typealias CreateRoomScreenViewModelType = StateStoreViewModel<CreateRoomScreenViewState, CreateRoomScreenViewAction>
|
||||
|
||||
class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreenViewModelProtocol {
|
||||
struct Parameters {
|
||||
var name = ""
|
||||
var topic = ""
|
||||
var isRoomPrivate = true
|
||||
var isKnockingOnly = false
|
||||
var avatarImageMedia: MediaInfo?
|
||||
var aliasLocalPart: String?
|
||||
}
|
||||
|
||||
private let userSession: UserSessionProtocol
|
||||
private var parameters: Parameters
|
||||
private let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
||||
private let analytics: AnalyticsService
|
||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||
@@ -36,25 +26,24 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(userSession: UserSessionProtocol,
|
||||
initialParameters: Parameters = .init(),
|
||||
init(isSpace: Bool,
|
||||
userSession: UserSessionProtocol,
|
||||
analytics: AnalyticsService,
|
||||
userIndicatorController: UserIndicatorControllerProtocol,
|
||||
appSettings: AppSettings) {
|
||||
self.userSession = userSession
|
||||
parameters = initialParameters
|
||||
mediaUploadingPreprocessor = MediaUploadingPreprocessor(appSettings: appSettings)
|
||||
self.analytics = analytics
|
||||
self.userIndicatorController = userIndicatorController
|
||||
|
||||
let bindings = CreateRoomScreenViewStateBindings(roomTopic: parameters.topic,
|
||||
isRoomPrivate: parameters.isRoomPrivate,
|
||||
isKnockingOnly: appSettings.knockingEnabled ? parameters.isKnockingOnly : false)
|
||||
let bindings = CreateRoomScreenViewStateBindings(roomTopic: "",
|
||||
selectedAccessType: .private)
|
||||
|
||||
super.init(initialViewState: CreateRoomScreenViewState(roomName: parameters.name,
|
||||
super.init(initialViewState: CreateRoomScreenViewState(isSpace: isSpace,
|
||||
roomName: "",
|
||||
serverName: userSession.clientProxy.userIDServerName ?? "",
|
||||
isKnockingFeatureEnabled: appSettings.knockingEnabled,
|
||||
aliasLocalPart: parameters.aliasLocalPart ?? roomAliasNameFromRoomDisplayName(roomName: parameters.name),
|
||||
aliasLocalPart: roomAliasNameFromRoomDisplayName(roomName: ""),
|
||||
bindings: bindings),
|
||||
mediaProvider: userSession.mediaProvider)
|
||||
|
||||
@@ -72,8 +61,7 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
|
||||
case .displayMediaPicker:
|
||||
actionsSubject.send(.displayMediaPicker)
|
||||
case .removeImage:
|
||||
parameters.avatarImageMedia = nil
|
||||
state.avatarURL = nil
|
||||
state.avatarMediaInfo = nil
|
||||
case .updateAliasLocalPart(let aliasLocalPart):
|
||||
state.aliasLocalPart = aliasLocalPart.lowercased()
|
||||
// If this has been called this means that the user wants a custom address not necessarily reflecting the name
|
||||
@@ -104,9 +92,8 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
|
||||
let mediaInfo = try await mediaUploadingPreprocessor.processMedia(at: fileURL, maxUploadSize: maxUploadSize).get()
|
||||
|
||||
switch mediaInfo {
|
||||
case .image(_, let thumbnailURL, _):
|
||||
parameters.avatarImageMedia = mediaInfo
|
||||
state.avatarURL = thumbnailURL
|
||||
case .image:
|
||||
state.avatarMediaInfo = mediaInfo
|
||||
default:
|
||||
break
|
||||
}
|
||||
@@ -123,34 +110,18 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
|
||||
// Reset the state related to public rooms if the user choses the room to be empty
|
||||
context.$viewState
|
||||
.dropFirst()
|
||||
.map(\.bindings.isRoomPrivate)
|
||||
.map(\.bindings.selectedAccessType)
|
||||
.removeDuplicates()
|
||||
.filter { $0 }
|
||||
.filter(\.isPrivate)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] _ in
|
||||
guard let self else { return }
|
||||
state.bindings.isKnockingOnly = false
|
||||
state.aliasErrors = []
|
||||
state.aliasLocalPart = roomAliasNameFromRoomDisplayName(roomName: state.roomName)
|
||||
syncNameAndAlias = true
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
context.$viewState
|
||||
.throttle(for: 0.5, scheduler: DispatchQueue.main, latest: true)
|
||||
.removeDuplicates { old, new in
|
||||
old.roomName == new.roomName &&
|
||||
old.bindings.roomTopic == new.bindings.roomTopic &&
|
||||
old.bindings.isRoomPrivate == new.bindings.isRoomPrivate &&
|
||||
old.bindings.isKnockingOnly == new.bindings.isKnockingOnly &&
|
||||
old.aliasLocalPart == new.aliasLocalPart
|
||||
}
|
||||
.sink { [weak self] state in
|
||||
guard let self else { return }
|
||||
updateParameters(state: state)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
context.$viewState
|
||||
.map(\.aliasLocalPart)
|
||||
.removeDuplicates()
|
||||
@@ -160,8 +131,7 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
|
||||
return
|
||||
}
|
||||
|
||||
guard state.isKnockingFeatureEnabled,
|
||||
!state.bindings.isRoomPrivate,
|
||||
guard !state.bindings.selectedAccessType.isPrivate,
|
||||
let canonicalAlias = String.makeCanonicalAlias(aliasLocalPart: aliasLocalPart,
|
||||
serverName: state.serverName) else {
|
||||
// While is empty or private room we don't change or display the error
|
||||
@@ -195,30 +165,15 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
private func updateParameters(state: CreateRoomScreenViewState) {
|
||||
parameters.name = state.roomName
|
||||
parameters.topic = state.bindings.roomTopic
|
||||
parameters.isRoomPrivate = state.bindings.isRoomPrivate
|
||||
parameters.isKnockingOnly = state.bindings.isKnockingOnly
|
||||
if state.isKnockingFeatureEnabled, !state.aliasLocalPart.isEmpty {
|
||||
parameters.aliasLocalPart = state.aliasLocalPart
|
||||
} else {
|
||||
parameters.aliasLocalPart = nil
|
||||
}
|
||||
}
|
||||
|
||||
private func createRoom() async {
|
||||
defer {
|
||||
hideLoadingIndicator()
|
||||
}
|
||||
showLoadingIndicator()
|
||||
|
||||
// Since the parameters are throttled, we need to make sure that the latest values are used
|
||||
updateParameters(state: state)
|
||||
|
||||
// Better to double check the errors also when trying to create the room
|
||||
if state.isKnockingFeatureEnabled, !parameters.isRoomPrivate {
|
||||
guard let canonicalAlias = String.makeCanonicalAlias(aliasLocalPart: parameters.aliasLocalPart,
|
||||
if !state.bindings.selectedAccessType.isPrivate {
|
||||
guard let canonicalAlias = String.makeCanonicalAlias(aliasLocalPart: state.aliasLocalPart,
|
||||
serverName: state.serverName),
|
||||
isRoomAliasFormatValid(alias: canonicalAlias) else {
|
||||
state.aliasErrors = [.invalidSymbols]
|
||||
@@ -238,7 +193,7 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
|
||||
}
|
||||
|
||||
let avatarURL: URL?
|
||||
if let media = parameters.avatarImageMedia {
|
||||
if let media = state.avatarMediaInfo {
|
||||
switch await userSession.clientProxy.uploadMedia(media) {
|
||||
case .success(let url):
|
||||
avatarURL = URL(string: url)
|
||||
@@ -263,14 +218,13 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
|
||||
avatarURL = nil
|
||||
}
|
||||
|
||||
switch await userSession.clientProxy.createRoom(name: parameters.name,
|
||||
topic: parameters.topic.isBlank ? nil : parameters.topic,
|
||||
isRoomPrivate: parameters.isRoomPrivate,
|
||||
// As of right now we don't want to make private rooms with the knock rule
|
||||
isKnockingOnly: parameters.isRoomPrivate ? false : parameters.isKnockingOnly,
|
||||
switch await userSession.clientProxy.createRoom(name: state.roomName,
|
||||
topic: state.bindings.roomTopic.isBlank ? nil : state.bindings.roomTopic,
|
||||
accessType: state.bindings.selectedAccessType,
|
||||
isSpace: state.isSpace,
|
||||
userIDs: [], // The invite users screen is shown next so we don't need to invite anyone right now.
|
||||
avatarURL: avatarURL,
|
||||
aliasLocalPart: parameters.isRoomPrivate ? nil : parameters.aliasLocalPart) {
|
||||
aliasLocalPart: state.bindings.selectedAccessType.isPrivate ? nil : state.aliasLocalPart) {
|
||||
case .success(let roomID):
|
||||
guard case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(roomID) else {
|
||||
state.bindings.alertInfo = AlertInfo(id: .failedCreatingRoom,
|
||||
|
||||
@@ -39,17 +39,15 @@ struct CreateRoomScreen: View {
|
||||
Form {
|
||||
roomSection
|
||||
topicSection
|
||||
securitySection
|
||||
if context.viewState.isKnockingFeatureEnabled,
|
||||
!context.isRoomPrivate {
|
||||
roomAccessSection
|
||||
roomAccessSection
|
||||
if !context.selectedAccessType.isPrivate {
|
||||
roomAliasSection
|
||||
}
|
||||
}
|
||||
.compoundList()
|
||||
.track(screen: .CreateRoom)
|
||||
.scrollDismissesKeyboard(.immediately)
|
||||
.navigationTitle(L10n.screenCreateRoomTitle)
|
||||
.navigationTitle(context.viewState.isSpace ? L10n.screenCreateRoomNewSpaceTitle : L10n.screenCreateRoomNewRoomTitle)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar { toolbar }
|
||||
.alert(item: $context.alertInfo)
|
||||
@@ -60,15 +58,20 @@ struct CreateRoomScreen: View {
|
||||
Section {
|
||||
HStack(alignment: .center, spacing: 16) {
|
||||
roomAvatarButton
|
||||
let nameLabel = if #available(iOS 26, *) {
|
||||
L10n.commonName
|
||||
} else {
|
||||
L10n.commonName.uppercased()
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text(L10n.screenCreateRoomRoomNameLabel.uppercased())
|
||||
Text(nameLabel)
|
||||
.padding(.leading, ListRowPadding.horizontal)
|
||||
.compoundListSectionHeader()
|
||||
|
||||
TextField(L10n.screenCreateRoomRoomNameLabel,
|
||||
TextField(L10n.commonName,
|
||||
text: roomNameBinding,
|
||||
prompt: Text(L10n.commonRoomNamePlaceholder).foregroundColor(.compound.textSecondary),
|
||||
prompt: Text(L10n.screenCreateRoomNamePlaceholder).foregroundColor(.compound.textSecondary),
|
||||
axis: .horizontal)
|
||||
.font(.compound.bodyLG)
|
||||
.foregroundStyle(.compound.textPrimary)
|
||||
@@ -90,7 +93,7 @@ struct CreateRoomScreen: View {
|
||||
focus = nil
|
||||
context.showAttachmentConfirmationDialog = true
|
||||
} label: {
|
||||
if let url = context.viewState.avatarURL {
|
||||
if let url = context.viewState.avatarMediaInfo?.thumbnailURL {
|
||||
AsyncImage(url: url) { image in
|
||||
image
|
||||
.resizable()
|
||||
@@ -118,7 +121,7 @@ struct CreateRoomScreen: View {
|
||||
}
|
||||
.accessibilityIdentifier(A11yIdentifiers.createRoomScreen.mediaPicker)
|
||||
|
||||
if context.viewState.avatarURL != nil {
|
||||
if context.viewState.avatarMediaInfo?.thumbnailURL != nil {
|
||||
Button(L10n.actionRemove, role: .destructive) {
|
||||
context.send(viewAction: .removeImage)
|
||||
}
|
||||
@@ -128,7 +131,7 @@ struct CreateRoomScreen: View {
|
||||
|
||||
private var topicSection: some View {
|
||||
Section {
|
||||
ListRow(label: .plain(title: L10n.commonTopicPlaceholder),
|
||||
ListRow(label: .plain(title: L10n.screenCreateRoomTopicPlaceholder),
|
||||
kind: .textField(text: $context.roomTopic, axis: .vertical))
|
||||
.lineLimit(3, reservesSpace: false)
|
||||
.focused($focus, equals: .topic)
|
||||
@@ -139,34 +142,16 @@ struct CreateRoomScreen: View {
|
||||
}
|
||||
}
|
||||
|
||||
private var securitySection: some View {
|
||||
Section {
|
||||
ListRow(label: .default(title: L10n.screenCreateRoomPrivateOptionTitle,
|
||||
description: L10n.screenCreateRoomPrivateOptionDescription,
|
||||
icon: \.lock,
|
||||
iconAlignment: .top),
|
||||
kind: .selection(isSelected: context.isRoomPrivate) { context.isRoomPrivate = true })
|
||||
ListRow(label: .default(title: L10n.screenCreateRoomPublicOptionTitle,
|
||||
description: L10n.screenCreateRoomPublicOptionDescription,
|
||||
icon: \.public,
|
||||
iconAlignment: .top),
|
||||
kind: .selection(isSelected: !context.isRoomPrivate) { context.isRoomPrivate = false })
|
||||
} header: {
|
||||
Text(L10n.screenCreateRoomRoomVisibilitySectionTitle)
|
||||
.compoundListSectionHeader()
|
||||
}
|
||||
}
|
||||
|
||||
private var roomAccessSection: some View {
|
||||
Section {
|
||||
ListRow(label: .plain(title: L10n.screenCreateRoomRoomAccessSectionAnyoneOptionTitle,
|
||||
description: L10n.screenCreateRoomRoomAccessSectionAnyoneOptionDescription),
|
||||
kind: .selection(isSelected: !context.isKnockingOnly) { context.isKnockingOnly = false })
|
||||
ListRow(label: .plain(title: L10n.screenCreateRoomRoomAccessSectionKnockingOptionTitle,
|
||||
description: L10n.screenCreateRoomRoomAccessSectionKnockingOptionDescription),
|
||||
kind: .selection(isSelected: context.isKnockingOnly) { context.isKnockingOnly = true })
|
||||
ForEach(context.viewState.availableAccessTypes, id: \.self) { accessType in
|
||||
CreateRoomAccessRow(access: accessType,
|
||||
isSelected: context.selectedAccessType == accessType) {
|
||||
context.selectedAccessType = accessType
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.screenCreateRoomRoomAccessSectionHeader)
|
||||
Text(L10n.screenCreateRoomRoomAccessSectionTitle)
|
||||
.compoundListSectionHeader()
|
||||
}
|
||||
}
|
||||
@@ -207,48 +192,129 @@ struct CreateRoomScreen: View {
|
||||
}
|
||||
}
|
||||
|
||||
private struct CreateRoomAccessRow: View {
|
||||
let access: CreateRoomAccessType
|
||||
let isSelected: Bool
|
||||
let onSelection: () -> Void
|
||||
|
||||
var title: String {
|
||||
switch access {
|
||||
case .public:
|
||||
L10n.screenCreateRoomRoomAccessSectionPublicOptionTitle
|
||||
case .askToJoin:
|
||||
L10n.screenCreateRoomRoomAccessSectionKnockingOptionTitle
|
||||
case .private:
|
||||
L10n.screenCreateRoomRoomAccessSectionPrivateOptionTitle
|
||||
}
|
||||
}
|
||||
|
||||
var description: String {
|
||||
switch access {
|
||||
case .public:
|
||||
L10n.screenCreateRoomRoomAccessSectionPublicOptionDescription
|
||||
case .askToJoin:
|
||||
L10n.screenCreateRoomRoomAccessSectionKnockingOptionDescription
|
||||
case .private:
|
||||
L10n.screenCreateRoomRoomAccessSectionPrivateOptionDescription
|
||||
}
|
||||
}
|
||||
|
||||
var icon: KeyPath<CompoundIcons, Image> {
|
||||
switch access {
|
||||
case .public:
|
||||
\.public
|
||||
case .askToJoin:
|
||||
\.userAdd
|
||||
case .private:
|
||||
\.lock
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ListRow(label: .default(title: title,
|
||||
description: description,
|
||||
icon: icon,
|
||||
iconAlignment: .top),
|
||||
kind: .selection(isSelected: isSelected,
|
||||
action: onSelection))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = {
|
||||
AppSettings.resetAllSettings()
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com"))))
|
||||
return CreateRoomScreenViewModel(userSession: userSession,
|
||||
initialParameters: .init(),
|
||||
return CreateRoomScreenViewModel(isSpace: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}()
|
||||
|
||||
static let spaceViewModel = {
|
||||
AppSettings.resetAllSettings()
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com"))))
|
||||
return CreateRoomScreenViewModel(isSpace: true,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}()
|
||||
|
||||
static let publicRoomViewModel = {
|
||||
AppSettings.resetAllSettings()
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com"))))
|
||||
ServiceLocator.shared.settings.knockingEnabled = true
|
||||
return CreateRoomScreenViewModel(userSession: userSession,
|
||||
initialParameters: .init(isRoomPrivate: false),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
viewModel.context.selectedAccessType = .public
|
||||
return viewModel
|
||||
}()
|
||||
|
||||
static let askToJoinViewModel = {
|
||||
AppSettings.resetAllSettings()
|
||||
let appSettings = AppSettings()
|
||||
appSettings.knockingEnabled = true
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com"))))
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: appSettings)
|
||||
viewModel.context.selectedAccessType = .askToJoin
|
||||
return viewModel
|
||||
}()
|
||||
|
||||
static let publicRoomInvalidAliasViewModel = {
|
||||
AppSettings.resetAllSettings()
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com"))))
|
||||
ServiceLocator.shared.settings.knockingEnabled = true
|
||||
return CreateRoomScreenViewModel(userSession: userSession,
|
||||
initialParameters: .init(isRoomPrivate: false, aliasLocalPart: "#:"),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
viewModel.context.selectedAccessType = .public
|
||||
viewModel.context.send(viewAction: .updateAliasLocalPart("#:"))
|
||||
return viewModel
|
||||
}()
|
||||
|
||||
static let publicRoomExistingAliasViewModel = {
|
||||
AppSettings.resetAllSettings()
|
||||
let clientProxy = ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com"))
|
||||
clientProxy.isAliasAvailableReturnValue = .success(false)
|
||||
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
ServiceLocator.shared.settings.knockingEnabled = true
|
||||
return CreateRoomScreenViewModel(userSession: userSession,
|
||||
initialParameters: .init(isRoomPrivate: false, aliasLocalPart: "existing"),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
viewModel.context.selectedAccessType = .public
|
||||
viewModel.context.send(viewAction: .updateAliasLocalPart("existing"))
|
||||
return viewModel
|
||||
}()
|
||||
|
||||
static var previews: some View {
|
||||
@@ -257,11 +323,21 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
||||
}
|
||||
.previewDisplayName("Create Room")
|
||||
|
||||
NavigationStack {
|
||||
CreateRoomScreen(context: spaceViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Create Space")
|
||||
|
||||
NavigationStack {
|
||||
CreateRoomScreen(context: publicRoomViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Create Public Room")
|
||||
|
||||
NavigationStack {
|
||||
CreateRoomScreen(context: askToJoinViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Create Knockable Room")
|
||||
|
||||
NavigationStack {
|
||||
CreateRoomScreen(context: publicRoomInvalidAliasViewModel.context)
|
||||
}
|
||||
|
||||
+10
-5
@@ -48,13 +48,15 @@ struct SecurityAndPrivacyScreen: View {
|
||||
Section {
|
||||
ListRow(label: .default(title: L10n.screenSecurityAndPrivacyRoomAccessAnyoneOptionTitle,
|
||||
description: L10n.screenSecurityAndPrivacyRoomAccessAnyoneOptionDescription,
|
||||
icon: \.public),
|
||||
icon: \.public,
|
||||
iconAlignment: .top),
|
||||
kind: .selection(isSelected: context.desiredSettings.accessType == .anyone) { context.desiredSettings.accessType = .anyone })
|
||||
|
||||
if context.viewState.isSpaceMembersOptionAvailable {
|
||||
ListRow(label: .default(title: L10n.screenSecurityAndPrivacyRoomAccessSpaceMembersOptionTitle,
|
||||
description: context.viewState.spaceMembersDescription,
|
||||
icon: \.space),
|
||||
icon: \.space,
|
||||
iconAlignment: .top),
|
||||
kind: .selection(isSelected: context.desiredSettings.accessType.isSpaceMembers) {
|
||||
context.send(viewAction: .selectedSpaceMembersAccess)
|
||||
})
|
||||
@@ -74,7 +76,8 @@ struct SecurityAndPrivacyScreen: View {
|
||||
|
||||
ListRow(label: .default(title: L10n.screenSecurityAndPrivacyRoomAccessInviteOnlyOptionTitle,
|
||||
description: L10n.screenSecurityAndPrivacyRoomAccessInviteOnlyOptionDescription,
|
||||
icon: \.lock),
|
||||
icon: \.lock,
|
||||
iconAlignment: .top),
|
||||
kind: .selection(isSelected: context.desiredSettings.accessType == .inviteOnly) { context.desiredSettings.accessType = .inviteOnly })
|
||||
} header: {
|
||||
Text(L10n.screenSecurityAndPrivacyRoomAccessSectionHeader)
|
||||
@@ -94,7 +97,8 @@ struct SecurityAndPrivacyScreen: View {
|
||||
private var askToJoinOption: some View {
|
||||
ListRow(label: .default(title: L10n.screenSecurityAndPrivacyAskToJoinOptionTitle,
|
||||
description: L10n.screenSecurityAndPrivacyAskToJoinOptionDescription,
|
||||
icon: \.userAdd),
|
||||
icon: \.userAdd,
|
||||
iconAlignment: .top),
|
||||
kind: .selection(isSelected: context.desiredSettings.accessType == .askToJoin) { context.desiredSettings.accessType = .askToJoin })
|
||||
.disabled(!context.viewState.isKnockingEnabled)
|
||||
}
|
||||
@@ -102,7 +106,8 @@ struct SecurityAndPrivacyScreen: View {
|
||||
private var askToJoinWithSpaceMembersOption: some View {
|
||||
ListRow(label: .default(title: L10n.screenSecurityAndPrivacyAskToJoinOptionTitle,
|
||||
description: context.viewState.askToJoinWithSpaceMembersDescription,
|
||||
icon: \.userAdd),
|
||||
icon: \.userAdd,
|
||||
iconAlignment: .top),
|
||||
kind: .selection(isSelected: context.desiredSettings.accessType.isAskToJoinWithSpaceMembers) { context.send(viewAction: .selectedAskToJoinWithSpaceMembersAccess) })
|
||||
.disabled(!context.viewState.isAskToJoinWithSpaceMembersOptionSelectable)
|
||||
}
|
||||
|
||||
@@ -474,25 +474,26 @@ class ClientProxy: ClientProxyProtocol {
|
||||
|
||||
func createRoom(name: String,
|
||||
topic: String?,
|
||||
isRoomPrivate: Bool,
|
||||
isKnockingOnly: Bool,
|
||||
accessType: CreateRoomAccessType,
|
||||
isSpace: Bool,
|
||||
userIDs: [String],
|
||||
avatarURL: URL?,
|
||||
aliasLocalPart: String?) async -> Result<String, ClientProxyError> {
|
||||
do {
|
||||
let parameters = CreateRoomParameters(name: name,
|
||||
topic: topic,
|
||||
isEncrypted: isRoomPrivate,
|
||||
isEncrypted: accessType.isEncrypted,
|
||||
isDirect: false,
|
||||
visibility: isRoomPrivate ? .private : .public,
|
||||
preset: isRoomPrivate ? .privateChat : .publicChat,
|
||||
visibility: accessType.visibility,
|
||||
preset: accessType.preset,
|
||||
invite: userIDs,
|
||||
avatar: avatarURL?.absoluteString,
|
||||
powerLevelContentOverride: isKnockingOnly ? Self.knockingRoomCreationPowerLevelOverrides : Self.roomCreationPowerLevelOverrides,
|
||||
joinRuleOverride: isKnockingOnly ? .knock : nil,
|
||||
historyVisibilityOverride: isRoomPrivate ? .invited : nil,
|
||||
powerLevelContentOverride: accessType == .askToJoin ? Self.knockingRoomCreationPowerLevelOverrides : Self.roomCreationPowerLevelOverrides,
|
||||
joinRuleOverride: accessType.joinRuleOverride,
|
||||
historyVisibilityOverride: accessType.historyVisibilityOverride,
|
||||
// This is an FFI naming mistake, what is required is the `aliasLocalPart` not the whole alias
|
||||
canonicalAlias: aliasLocalPart)
|
||||
canonicalAlias: aliasLocalPart,
|
||||
isSpace: isSpace)
|
||||
let roomID = try await client.createRoom(request: parameters)
|
||||
|
||||
await waitForRoomToSync(roomID: roomID)
|
||||
@@ -1300,3 +1301,35 @@ private extension TimelineMediaVisibility {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension CreateRoomAccessType {
|
||||
var isEncrypted: Bool {
|
||||
switch self {
|
||||
case .public:
|
||||
false
|
||||
case .askToJoin, .private:
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
var visibility: RoomVisibility {
|
||||
isPrivate ? .private : .public
|
||||
}
|
||||
|
||||
var preset: RoomPreset {
|
||||
isPrivate ? .privateChat : .publicChat
|
||||
}
|
||||
|
||||
var historyVisibilityOverride: RoomHistoryVisibility? {
|
||||
isPrivate ? .invited : nil
|
||||
}
|
||||
|
||||
var joinRuleOverride: JoinRule? {
|
||||
switch self {
|
||||
case .askToJoin:
|
||||
.knock
|
||||
case .private, .public:
|
||||
nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,21 @@ enum SlidingSyncConstants {
|
||||
static let maximumVisibleRangeSize = 30
|
||||
}
|
||||
|
||||
enum CreateRoomAccessType: CaseIterable {
|
||||
case `public`
|
||||
case askToJoin
|
||||
case `private`
|
||||
|
||||
var isPrivate: Bool {
|
||||
switch self {
|
||||
case .private:
|
||||
true
|
||||
case .public, .askToJoin:
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This struct represents the configuration that we are using to register the application through Pusher to Sygnal
|
||||
/// using the Matrix Rust SDK, more info here:
|
||||
/// https://github.com/matrix-org/sygnal
|
||||
@@ -156,8 +171,8 @@ protocol ClientProxyProtocol: AnyObject {
|
||||
|
||||
func createRoom(name: String,
|
||||
topic: String?,
|
||||
isRoomPrivate: Bool,
|
||||
isKnockingOnly: Bool,
|
||||
accessType: CreateRoomAccessType,
|
||||
isSpace: Bool,
|
||||
userIDs: [String],
|
||||
avatarURL: URL?,
|
||||
aliasLocalPart: String?) async -> Result<String, ClientProxyError>
|
||||
|
||||
@@ -651,14 +651,16 @@ class MockScreen: Identifiable {
|
||||
return navigationStackCoordinator
|
||||
case .startChatFlow:
|
||||
let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com"))
|
||||
clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReturnValue = .success("!new-room:client.com")
|
||||
clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReturnValue =
|
||||
.success("!new-room:client.com")
|
||||
clientProxy.roomForIdentifierClosure = { roomID in .joined(JoinedRoomProxyMock(.init(id: roomID, members: []))) }
|
||||
|
||||
let userDiscoveryService = UserDiscoveryServiceMock()
|
||||
userDiscoveryService.searchProfilesWithReturnValue = .success([.mockBob, .mockBobby])
|
||||
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let flowCoordinator = StartChatFlowCoordinator(userDiscoveryService: userDiscoveryService,
|
||||
let flowCoordinator = StartChatFlowCoordinator(isSpace: false,
|
||||
userDiscoveryService: userDiscoveryService,
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
flowParameters: CommonFlowParameters(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
|
||||
bugReportService: BugReportServiceMock(.init()),
|
||||
|
||||
Reference in New Issue
Block a user