Create room redesign and refactor (#4942)

This commit is contained in:
Mauro
2026-01-12 18:49:31 +01:00
committed by GitHub
parent f42c063e50
commit 4cfedefcca
77 changed files with 463 additions and 348 deletions
+1 -1
View File
@@ -9855,7 +9855,7 @@
repositoryURL = "https://github.com/pointfreeco/swift-snapshot-testing";
requirement = {
kind = upToNextMinorVersion;
minimumVersion = 1.18.3;
minimumVersion = 1.18.7;
};
};
EC6D0C817B1C21D9D096505A /* XCRemoteSwiftPackageReference "Version" */ = {
@@ -532,18 +532,21 @@
"screen_create_poll_remove_accessibility_label" = "Remove %1$@";
"screen_create_poll_settings_section_title" = "Settings";
"screen_create_room_error_creating_space" = "The space could not be created because of an unknown error. Try again later.";
"screen_create_room_name_placeholder" = "Add name…";
"screen_create_room_new_room_title" = "New room";
"screen_create_room_new_space_title" = "New space";
"screen_create_room_public_option_short_description" = "Anyone can join.";
"screen_create_room_room_access_section_anyone_option_description" = "Anyone can join.";
"screen_create_room_room_access_section_anyone_option_title" = "Anyone";
"screen_create_room_room_access_section_header" = "Room Access";
"screen_create_room_room_access_section_knocking_option_description" = "Anyone can ask to join but an administrator or a moderator must accept the request.";
"screen_create_room_room_access_section_knocking_option_title" = "Ask to join";
"screen_create_room_room_access_section_knocking_option_title" = "Allow ask to join";
"screen_create_room_room_access_section_private_option_description" = "Only people invited can join.";
"screen_create_room_room_access_section_private_option_title" = "Private";
"screen_create_room_room_access_section_public_option_description" = "Anyone can join.";
"screen_create_room_room_access_section_public_option_title" = "Public";
"screen_create_room_room_access_section_title" = "Who has access";
"screen_create_room_room_address_section_footer" = "In order for this room to be visible in the public room directory, you will need a room address.";
"screen_create_room_room_address_section_title" = "Room address";
"screen_create_room_room_address_section_footer" = "You’ll need an address in order to make it visible in the public directory.";
"screen_create_room_room_address_section_title" = "Address";
"screen_create_room_room_visibility_section_title" = "Room visibility";
"screen_create_room_title_new_space" = "New space";
"screen_create_room_topic_placeholder" = "Add description…";
"screen_decline_and_block_block_user_option_description" = "You will not see any messages or room invites from this user";
"screen_decline_and_block_report_user_option_description" = "Report this room to your account provider.";
"screen_edit_room_address_room_address_section_footer" = "You’ll need an address in order to make it visible in the public directory.";
@@ -681,6 +684,7 @@
"screen_security_and_privacy_add_room_address_action" = "Add address";
"screen_security_and_privacy_ask_to_join_multiple_spaces_members_option_description" = "Anyone in authorized spaces can join, but everyone else must request access.";
"screen_security_and_privacy_ask_to_join_option_description" = "Everyone must request access.";
"screen_security_and_privacy_ask_to_join_option_title" = "Ask to join";
"screen_security_and_privacy_ask_to_join_single_space_members_option_description" = "Anyone in %1$@ can join, but everyone else must request access.";
"screen_security_and_privacy_enable_encryption_alert_confirm_button_title" = "Yes, enable encryption";
"screen_security_and_privacy_enable_encryption_alert_description" = "Once enabled, encryption for a room cannot be disabled, Message history will only be visible for room members since they were invited or since they joined the room.\nNo one besides the room members will be able to read messages. This may prevent bots and bridges to work correctly.\nWe do not recommend enabling encryption for rooms that anyone can find and join.";
@@ -688,6 +692,7 @@
"screen_security_and_privacy_encryption_section_footer" = "Once enabled, encryption cannot be disabled.";
"screen_security_and_privacy_encryption_toggle_title" = "Enable end-to-end encryption";
"screen_security_and_privacy_room_access_anyone_option_description" = "Anyone can join.";
"screen_security_and_privacy_room_access_anyone_option_title" = "Anyone";
"screen_security_and_privacy_room_access_footer" = "Choose which spaces’ members can join this room without an invitation. %1$@";
"screen_security_and_privacy_room_access_invite_only_option_description" = "Only invited people can join.";
"screen_security_and_privacy_room_access_invite_only_option_title" = "Invite only";
@@ -1424,9 +1429,7 @@
"screen_room_details_profile_row_title" = "Profile";
"screen_room_details_security_and_privacy_title" = "Security & privacy";
"screen_roomlist_knock_event_sent_description" = "Request to join sent";
"screen_security_and_privacy_ask_to_join_option_title" = "Ask to join";
"screen_security_and_privacy_encryption_section_header" = "Encryption";
"screen_security_and_privacy_room_access_anyone_option_title" = "Anyone";
"screen_security_and_privacy_room_access_footer_manage_spaces_action" = "Manage spaces";
"screen_security_and_privacy_room_address_section_footer" = "You’ll need an address in order to make it visible in the public directory.";
"screen_space_settings_leave_space" = "Leave space";
@@ -1447,8 +1450,6 @@
"screen_create_account_title" = "Create account";
"screen_create_poll_cancel_confirmation_content_ios" = "Your changes won’t be saved";
"screen_create_room_add_people_title" = "Invite people";
"screen_create_room_room_name_label" = "Room name";
"screen_create_room_title" = "Create a room";
"screen_deactivate_account_title" = "Deactivate account";
"screen_dm_details_block_alert_action" = "Block";
"screen_dm_details_block_alert_description" = "Blocked users won't be able to send you messages and all their messages will be hidden. You can unblock them anytime.";
@@ -532,18 +532,21 @@
"screen_create_poll_remove_accessibility_label" = "Remove %1$@";
"screen_create_poll_settings_section_title" = "Settings";
"screen_create_room_error_creating_space" = "The space could not be created because of an unknown error. Try again later.";
"screen_create_room_name_placeholder" = "Add name…";
"screen_create_room_new_room_title" = "New room";
"screen_create_room_new_space_title" = "New space";
"screen_create_room_public_option_short_description" = "Anyone can join.";
"screen_create_room_room_access_section_anyone_option_description" = "Anyone can join.";
"screen_create_room_room_access_section_anyone_option_title" = "Anyone";
"screen_create_room_room_access_section_header" = "Room Access";
"screen_create_room_room_access_section_knocking_option_description" = "Anyone can ask to join but an administrator or a moderator must accept the request.";
"screen_create_room_room_access_section_knocking_option_title" = "Ask to join";
"screen_create_room_room_access_section_knocking_option_title" = "Allow ask to join";
"screen_create_room_room_access_section_private_option_description" = "Only people invited can join.";
"screen_create_room_room_access_section_private_option_title" = "Private";
"screen_create_room_room_access_section_public_option_description" = "Anyone can join.";
"screen_create_room_room_access_section_public_option_title" = "Public";
"screen_create_room_room_access_section_title" = "Who has access";
"screen_create_room_room_address_section_footer" = "In order for this room to be visible in the public room directory, you will need a room address.";
"screen_create_room_room_address_section_title" = "Room address";
"screen_create_room_room_address_section_footer" = "You’ll need an address in order to make it visible in the public directory.";
"screen_create_room_room_address_section_title" = "Address";
"screen_create_room_room_visibility_section_title" = "Room visibility";
"screen_create_room_title_new_space" = "New space";
"screen_create_room_topic_placeholder" = "Add description…";
"screen_decline_and_block_block_user_option_description" = "You will not see any messages or room invites from this user";
"screen_decline_and_block_report_user_option_description" = "Report this room to your account provider.";
"screen_edit_room_address_room_address_section_footer" = "You’ll need an address in order to make it visible in the public directory.";
@@ -681,6 +684,7 @@
"screen_security_and_privacy_add_room_address_action" = "Add address";
"screen_security_and_privacy_ask_to_join_multiple_spaces_members_option_description" = "Anyone in authorised spaces can join, but everyone else must request access.";
"screen_security_and_privacy_ask_to_join_option_description" = "Everyone must request access.";
"screen_security_and_privacy_ask_to_join_option_title" = "Ask to join";
"screen_security_and_privacy_ask_to_join_single_space_members_option_description" = "Anyone in %1$@ can join, but everyone else must request access.";
"screen_security_and_privacy_enable_encryption_alert_confirm_button_title" = "Yes, enable encryption";
"screen_security_and_privacy_enable_encryption_alert_description" = "Once enabled, encryption for a room cannot be disabled, Message history will only be visible for room members since they were invited or since they joined the room.\nNo one besides the room members will be able to read messages. This may prevent bots and bridges to work correctly.\nWe do not recommend enabling encryption for rooms that anyone can find and join.";
@@ -688,6 +692,7 @@
"screen_security_and_privacy_encryption_section_footer" = "Once enabled, encryption cannot be disabled.";
"screen_security_and_privacy_encryption_toggle_title" = "Enable end-to-end encryption";
"screen_security_and_privacy_room_access_anyone_option_description" = "Anyone can join.";
"screen_security_and_privacy_room_access_anyone_option_title" = "Anyone";
"screen_security_and_privacy_room_access_footer" = "Choose which spaces’ members can join this room without an invitation. %1$@";
"screen_security_and_privacy_room_access_invite_only_option_description" = "Only invited people can join.";
"screen_security_and_privacy_room_access_invite_only_option_title" = "Invite only";
@@ -1424,9 +1429,7 @@
"screen_room_details_profile_row_title" = "Profile";
"screen_room_details_security_and_privacy_title" = "Security & privacy";
"screen_roomlist_knock_event_sent_description" = "Request to join sent";
"screen_security_and_privacy_ask_to_join_option_title" = "Ask to join";
"screen_security_and_privacy_encryption_section_header" = "Encryption";
"screen_security_and_privacy_room_access_anyone_option_title" = "Anyone";
"screen_security_and_privacy_room_access_footer_manage_spaces_action" = "Manage spaces";
"screen_security_and_privacy_room_address_section_footer" = "You’ll need an address in order to make it visible in the public directory.";
"screen_space_settings_leave_space" = "Leave space";
@@ -1447,8 +1450,6 @@
"screen_create_account_title" = "Create account";
"screen_create_poll_cancel_confirmation_content_ios" = "Your changes won’t be saved";
"screen_create_room_add_people_title" = "Invite people";
"screen_create_room_room_name_label" = "Room name";
"screen_create_room_title" = "Create a room";
"screen_deactivate_account_title" = "Deactivate account";
"screen_dm_details_block_alert_action" = "Block";
"screen_dm_details_block_alert_description" = "Blocked users won't be able to send you messages and all their messages will be hidden. You can unblock them anytime.";
@@ -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)
+17 -15
View File
@@ -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
+1 -1
View File
@@ -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)
}
@@ -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()),
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f513aac79644e109b9cd0be2cf88e47362a70bdd39e92688fecbeab9d49e6414
size 157858
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f4dcf0f103278fa02af6c0d97f54791e14f25d1f5ec0e31eba5b080e21ce6858
size 187550
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e6e0f7572a47ad8fbc35bbe862e8d0634dd1357f4559bd54d6cb57d481485763
size 105733
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:abcffb042721e323327f3c70f4cdca89ff56654887293b2f9b2215cc486d465c
size 142332
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0f8d601c193367fe6e2fcbcafeea76da0481328d4c63d6c6f3a5106f9869cc0f
size 186260
oid sha256:c744f0ddc6f0e2cb2e2063cc0135d18a07f18d3a58314ee33651962295199225
size 158006
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fc25f9648a4c81cf21d0d420fb08825f96d6a54fc1eedc589c673866a67af784
size 224957
oid sha256:301081a176b706092f008e01c9a195dd58c7dc28adc7a42d38ee5e4c168cd1a2
size 189421
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:98c9a9ee2f1c0d78a24a5e096c11f72f2467ee22c66f9fa26b84272732eb339c
size 121268
oid sha256:7128f0417b2479525d2b35bfb4a501ac24690694f34841bc7ecf0d922741654d
size 109846
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:33ac15d63ca2d46ba68e087e712442761bc40feecba310825e661b97b59cfba2
size 144352
oid sha256:2a656ea4051f6a84282fca5f94978b199ec10df51dac04a7ad01c4ec55b6ae1f
size 146993
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9df32336b119eb58e4aa3a66280b944c6b54714509fab4d843fd16304d5292ab
size 180274
oid sha256:2c110232459827a0f641057ed1bfc6bfdb714134f3a658fa5acee9a9395bf62a
size 142457
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:21fc629bec2b888ab69f895fbb724c862383c67e5c8bfc3702f2a82ed046b5c3
size 219688
oid sha256:729a6515bc93eb02be4d04a60c07f3854875ab2404a282a5de8bd11ae9e86a89
size 165595
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f5b29aa43bf4f260ca92827715079a66a2a721329510bdadb6e732ad6bbc067b
size 114041
oid sha256:9d4b216831eb0595f1b3adbf0fade0d3db2e621d7922dbc647b62fc31ddc854f
size 88801
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5e00046d8a037d82931c163483cb96238476af973db9c36b66f6253b2dc94459
size 141659
oid sha256:c3cfa55a1a89d1905c5c21d5aeadce37d8554487054c2abe6c7d39323e7359c4
size 115037
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4c67f6b214cc6d321d3566b5c02f3f8a226bfd0745cd5092f45c7c3e1a73fb32
size 189590
oid sha256:ea061eb260fdeebd3d38a6f32c209345f45dfe3188bff5ebb4e5fefa26bd73e3
size 161310
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0018ab26fdd70f668853b04b10575aa018cb8dcb7cc3863350108354e0735121
size 227012
oid sha256:cc8f86bff3d1377c3ca9cca775c998a736a90211d7bb4d03439aa788bdc60160
size 197756
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:55dff7dd30c3bbe8ba5a4791ecd30bad37947f6209daf4ef2c8d0dcc154dc4b0
size 119611
oid sha256:c58d9126bdf00d7b64ac59b740645d456ad06fe66ff67f0e805eb4b140f0adc1
size 111712
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:33ac15d63ca2d46ba68e087e712442761bc40feecba310825e661b97b59cfba2
size 144352
oid sha256:3cc77ead9f512f919377e076cf95cba2678a0eb3d7c79395dfcdd33a82d32d3f
size 152160
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fdc1e9766a4dde854f05b4b42c3fe6d9faf451813a0230b10dfc241b20038ad7
size 138315
oid sha256:26726c399334387ad4f4f9603af729dd998d8aa40e31175def2b1719e3fe27ec
size 124557
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6f5c3ffd80810d57d9dceb6f64189b30cf5e60628b48e6968896dacfc53a0e17
size 161485
oid sha256:6f0b7534290fc773b98633250f05b434ef83c5f8243dd891de5bc58902941e28
size 142357
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:572a0649d2ba4efa3056ce34a822d4b1237891fc15a538f80ac51ff6948952cc
size 86254
oid sha256:37a460ee15e802c3a165053bba730a911518682570d0692c2f1228913936cb95
size 72654
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:968186dc96ec4e01fe52ff68ef441f85f48ad2d776ade28d983e650ec4b585c8
size 106420
oid sha256:8b1447dc8d94468cbb42821eea9bc4431ad6c26682a3ef3901d11d80bddaed9a
size 89114
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0152d34aba8e7c97fd38b48433a83dee21ceaea96aacbb070782d65a75bfc4ce
size 125037
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5e9b590ea56f6498031583f2eceda6c1269a697b532006d8226a5c0fb152ca41
size 142802
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c672bfb3589592b0cdcd373ce02e47ebcdaf695d8f1e009381f2c527f326aed2
size 73155
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b61ca92955968e5905967c5a62da3de062a561f608d6da3f7e200776a76ebfda
size 89771
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4a2874e02e5bae080c7f422b1359199f70f208d60735825416419882e8161b80
size 195179
oid sha256:f3a736a4d1805184d597e7e8209d72208cdb942015ca7f5b119b9be551a2d13a
size 195381
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c90113d2a9b105c70446f58fa0e766b1cfdb83d4dae9b4c34efbb575b2cc5d3e
size 233537
oid sha256:29a5425aee883225d39030768f821cf098084beb7f084998ffb25f914254d793
size 233810
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f7aa48ef3727c3fcc42a3b85a55417c217e50a4e50546b1fcb5bf92bb76152e3
size 132863
oid sha256:c6a7aff5bc662c961cef7b941387ee8fd55b382496d0225d63a7baf2a85b3f2b
size 133082
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c56c5e6bf983dfbcc5aba2b9474be0c2f6da8c8fd94ec37cf72d4d02e24e227e
size 164583
oid sha256:8ddfbe02c83239f4ea3c8b95eec2c6f508d835da6191ccd5170092eee12f630e
size 164847
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ca2c5743874b7c404c06b23bf133fb77d5f61812b1d55c2d5170b968915694ac
size 211272
oid sha256:ddfbaeef5378191153110b43f9f48f21182159a8c75fb9234de6f8c9257bdcdf
size 211601
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7ebf814a7cb3d144e7c5172fc889114d41929dacf8068458b62152f832ff426d
size 260403
oid sha256:36b336fb2119ba7753703704fc6ec507c429a389a820548e0c131e4b81c49fa6
size 260759
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:58da8eeaec2f85afc1ecf3e762c79d94008b5fe725ae72e00aaf965858ef6b7c
size 147164
oid sha256:44eee6824cf09e831336f8032fdbadbe023e47167edd16ca4332aab9a1e56887
size 147504
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b877cc4e12ac4afefb2ef0793f09919e9b81127a9eaa8cb221ebe2138b6b5a3f
size 179833
oid sha256:407dc0929ce569c6cc4f5f5163ab8c1eaa79f35784e6949f96010a6f761067d0
size 180458
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bf83cc61198e77aa658cc17d57dbc1019569b2552bb341b90034f7345939e588
size 198334
oid sha256:2b2cd165d6aa5fced734eb4207aa7dd252e47fce6f240f50b9434c5984e8e5a2
size 198692
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:afd52028661858843b63bc99f715bd91fc10d5fb22b8eee3117875afee29722b
size 248058
oid sha256:8da0db3f1929d2cdedb348146e751e5e2b29bb2a251e3777ecf227b06cbab0a5
size 248426
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cf7256d898a8329af1c1153250d1492e377021de91aacd23544fe83cbf16edb2
size 136320
oid sha256:2aa7d0bef7669c074cc9167e681a5e16fe29103f948d7e0959261f3ad1c0bb1b
size 136685
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1a48162c23078a6eac6e3afdbe16b8045bb558247ce956a18f8b8a84eb73ad9e
size 170604
oid sha256:bd5ff993dde6c617ef07b96e9d5841b13707f8d875c4a4c08f027359e65e7f0d
size 171197
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:200ee11a5dd56886be8381f1a129b67c250d47a6eaefaedf82127973bb4f8a16
size 173112
oid sha256:40718c83b3c6ae61aba5871cd179673684f38d516bf22070d2d19d920be5c228
size 173334
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7d348350791ed25e7e5db970a8128dda7f94c41216fb0f46f70f6c8b13540c78
size 213524
oid sha256:711acf1affdf3fb3ec7f80776d78669e7e6652aef359e2285645af4b73c4a851
size 213700
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:614305ed71ccea6133304c2b4caa9130437eb6ca55c7f01aba34cd78e678cca2
size 118448
oid sha256:c5fcdfd6cfb12d365e13d42d101da721104f8b92ac4c26750fc60edd4a11c4d7
size 118619
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:40bfe29281241156f291ebe633b913768441de7fa93d45e9cc98017373fcc842
size 176498
oid sha256:a6debe0c740afde8f727afdc6e84294fb6e5c793559924c2107c1b629894a2b4
size 176819
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:19ee4c8b01d1b18b962971ae0b1b950279863d12e832b572e2635837ace8f569
size 148707
oid sha256:3466617944665b46015917c82d4dc84b420e5c54b53403f55d9fe78962f61d56
size 148793
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:801c65df001931f413d10652a0934564b750cb7f2888760fe3ac8dd69461f649
size 177970
oid sha256:8d016464d8f6cd7017e84465531dc9622fb865e61b6dc6cd7f9a9538a76befb5
size 178171
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:108fa3154405e527b4f83dfb15f2d7720b375c349c8d9de50c4984192839ef26
size 93016
oid sha256:3914a0e0ed46c0d88bb1fc368239c518ae2cc5687a836d16b5dd4bc1d76faf3d
size 93124
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e23e9b6268efabf0082f700393f080c7ff520af0a77a5c59d526800fb8b0e1b7
size 132768
oid sha256:7f9eb7cc54ffed0a534a71b9ab2538fe3706c5d44a241429fffe05a5d7c1b197
size 132874
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9af7a48f714bec7db946bdf6762507aca1cd5609eadcb025857f7f3a7bd33179
size 198158
oid sha256:c448b9630b608cd0bc4ddae356b31d6b5983339ef001f86e25b33fd14dbe4853
size 198214
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:15dd87267271389ad687a635154757aba54788a2d47a1a89857495b7ceb3e093
size 250229
oid sha256:7a0a36c9c6c4e1d671a2d6b076b8eee9317ced03b3f42ce08e7e650b4fb34ff0
size 250367
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5ea2f6d3dfa555f7be8886086f8f9885e8a537599e5e130d8019e870b5018b2a
size 134575
oid sha256:a1713eacee2906eb96f6cca8e1f44eba015a28e4d83d074287d9509ebd958ba8
size 134682
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1ddc6ad69921913ef2f085c0cba06f5e731e727b36a8e9533825ee636315252a
size 169254
oid sha256:5a186f342d0fb66171e821d425f3c946abded3d27ec8a26f69c992f28f898ed1
size 169406
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c324010c3328fa225c4477f1bb27be733072e5d8cd3ce888db6220a6d3bda0e6
size 170487
oid sha256:0d1035d5d115c21eda179a45a159aa41590ec6234042323db1aa979982dddab2
size 170541
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3aaeb4185e78b1e6c4417bb7d76afac8421589f70fac6c486841b9974e386f09
size 215913
oid sha256:9857f9df82cf5938e55cd02442a0ecf781eca44623fe32e3735127410b2f652d
size 216055
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bbc7316079d963ff3b8bc1680ba8bdc8212985668552de2fca39a6c51d004ab0
size 115669
oid sha256:4e05ee29eca1bb23ad9238b8fdc1bd73a84901d12b234aaa2f7175f15dca22ef
size 115798
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:23fddbb25038c9fdc0bfa53d48e0093cd22f4bc02453d0c4e436a64d531eb388
size 153794
oid sha256:8184ca500b01f72d8fb86e034a4b0c2acfc9bcb884570f4ca3e266b7bce79542
size 153899
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:41045c5701b59ed3164a4999189cff0f74772e420e511fc4339f2c4b2933762b
size 151773
oid sha256:56168eb647c611eb0c214d3598e5d2518fa2933ed08f83d4ccd6c27be94ce1d1
size 151831
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8ce1a903be20de8485976cfcf5ad0f02458f8d1e95975bd83e05f946c5643200
size 182075
oid sha256:bec22e56da539e1350b33f5b8dd229b57358750c6604b84471ec954a2165ec12
size 182216
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e78110e0a62ad1e0f83afb2a7f0d4d0212393dddb3c69fbf986387b0d02335d9
size 104721
oid sha256:de97c82bf7ea565a982bc15425a77870152b7d048014751bf0850e9a416fec33
size 104828
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:77754ddf31e79f7366b80ddd8a80b64ba17d1ca158930cdc74a0f97efc567264
size 147104
oid sha256:81df879269cc8b8daa6dd7f174f290ed23e8f3d64a3e26fb4612b61db77a5907
size 147256
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b05a474e324855ae6a103ba650d11789b18ad5aabbf2690d5597302e58f1949b
size 160311
oid sha256:e971940a1cc8bec0e7485e3d320edc679bbb8ccb7968082809fabb4016c194d9
size 160528
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d697c4216ba4330a2a91ac058c2c96380b5f192496a1be2a33a652a922365977
size 195262
oid sha256:3c8ec3fe11e9bb64c153efb74debe250acc64e2935e500bcf04cae882cf0105b
size 195540
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1709e4b6fa0940284e48c4b8725c79361b5618fcdc3e49db8b87ccc5b0d874d9
size 105070
oid sha256:3f937fb0b3982bdea1697fa4059c94d729704cc1a31abd8c4e332e98cbe7d996
size 105278
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:aeb53763be78d707668492260db9328a5eb570fcb4ac02a00aa661406c729447
size 152085
oid sha256:dd46ac2966599153dbeca05d90ef59e1d9fe3cfbebea3726e3cbec97b89f50e2
size 152428
@@ -28,15 +28,20 @@ class CreateRoomScreenViewModelTests: XCTestCase {
clientProxy.roomForIdentifierClosure = { roomID in .joined(JoinedRoomProxyMock(.init(id: roomID))) }
userSession = UserSessionMock(.init(clientProxy: clientProxy))
ServiceLocator.shared.settings.knockingEnabled = true
let viewModel = CreateRoomScreenViewModel(userSession: userSession,
let viewModel = CreateRoomScreenViewModel(isSpace: false,
userSession: userSession,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: UserIndicatorControllerMock(),
appSettings: ServiceLocator.shared.settings)
self.viewModel = viewModel
}
override func tearDown() {
AppSettings.resetAllSettings()
}
func testDefaultSecurity() {
XCTAssertTrue(context.viewState.bindings.isRoomPrivate)
XCTAssertEqual(context.viewState.bindings.selectedAccessType, .private)
}
func testCreateRoomRequirements() {
@@ -49,11 +54,11 @@ class CreateRoomScreenViewModelTests: XCTestCase {
// Given a form with a blank topic.
context.send(viewAction: .updateRoomName("A"))
context.roomTopic = ""
context.isRoomPrivate = false
context.selectedAccessType = .private
XCTAssertTrue(context.viewState.canCreateRoom)
// When creating the room.
clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReturnValue = .success("1")
clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReturnValue = .success("1")
let deferred = deferFulfillment(viewModel.actions) { action in
guard case .createdRoom(let roomProxy) = action, roomProxy.id == "1" else { return false }
return true
@@ -62,26 +67,24 @@ class CreateRoomScreenViewModelTests: XCTestCase {
try await deferred.fulfill()
// Then the room should be created and a topic should not be set.
XCTAssertTrue(clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartCalled)
XCTAssertEqual(clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReceivedArguments?.name, "A")
XCTAssertNil(clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReceivedArguments?.topic,
XCTAssertTrue(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartCalled)
XCTAssertEqual(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.name, "A")
XCTAssertNil(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.topic,
"The topic should be sent as nil when it is empty.")
}
func testCreateKnockingRoom() async {
context.send(viewAction: .updateRoomName("A"))
context.roomTopic = "B"
context.isRoomPrivate = false
context.selectedAccessType = .askToJoin
// When setting the room as private we always reset the knocking state to the default value of false
// so we need to wait a main actor cycle to ensure the view state is updated
await Task.yield()
context.isKnockingOnly = true
XCTAssertTrue(context.viewState.canCreateRoom)
let expectation = expectation(description: "Wait for the room to be created")
clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartClosure = { _, _, isPrivate, isKnockingOnly, _, _, localAliasPart in
XCTAssertTrue(isKnockingOnly)
XCTAssertFalse(isPrivate)
clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartClosure = { _, _, accessType, _, _, _, localAliasPart in
XCTAssertEqual(accessType, .askToJoin)
XCTAssertEqual(localAliasPart, "a")
defer { expectation.fulfill() }
return .success("")
@@ -93,7 +96,7 @@ class CreateRoomScreenViewModelTests: XCTestCase {
func testCreatePublicRoomFailsForInvalidAlias() async throws {
context.send(viewAction: .updateRoomName("A"))
context.roomTopic = "B"
context.isRoomPrivate = false
context.selectedAccessType = .public
// When setting the room as private we always reset the alias
// so we need to wait a main actor cycle to ensure the view state is updated
await Task.yield()
@@ -109,14 +112,14 @@ class CreateRoomScreenViewModelTests: XCTestCase {
// blocked it
context.send(viewAction: .createRoom)
await Task.yield()
XCTAssertFalse(clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartCalled)
XCTAssertFalse(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartCalled)
}
func testCreatePublicRoomFailsForExistingAlias() async throws {
clientProxy.isAliasAvailableReturnValue = .success(false)
context.send(viewAction: .updateRoomName("A"))
context.roomTopic = "B"
context.isRoomPrivate = false
context.selectedAccessType = .public
// When setting the room as private we always reset the alias
// so we need to wait a main actor cycle to ensure the view state is updated
await Task.yield()
@@ -140,27 +143,11 @@ class CreateRoomScreenViewModelTests: XCTestCase {
context.send(viewAction: .createRoom)
await fulfillment(of: [expectation])
XCTAssertEqual(clientProxy.isAliasAvailableCallsCount, 2)
XCTAssertFalse(clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartCalled)
}
func testCreatePrivateRoomCantHaveKnockRule() async {
context.send(viewAction: .updateRoomName("A"))
context.roomTopic = "B"
context.isRoomPrivate = true
context.isKnockingOnly = true
context.send(viewAction: .createRoom)
let expectation = expectation(description: "Wait for the room to be created")
clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartClosure = { _, _, isPrivate, isKnockingOnly, _, _, _ in
XCTAssertFalse(isKnockingOnly)
XCTAssertTrue(isPrivate)
expectation.fulfill()
return .success("")
}
await fulfillment(of: [expectation])
XCTAssertFalse(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartCalled)
}
func testNameAndAddressSync() async {
context.isRoomPrivate = true
context.selectedAccessType = .private
await Task.yield()
context.send(viewAction: .updateRoomName("abc"))
XCTAssertEqual(context.viewState.aliasLocalPart, "abc")