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"; repositoryURL = "https://github.com/pointfreeco/swift-snapshot-testing";
requirement = { requirement = {
kind = upToNextMinorVersion; kind = upToNextMinorVersion;
minimumVersion = 1.18.3; minimumVersion = 1.18.7;
}; };
}; };
EC6D0C817B1C21D9D096505A /* XCRemoteSwiftPackageReference "Version" */ = { EC6D0C817B1C21D9D096505A /* XCRemoteSwiftPackageReference "Version" */ = {
@@ -532,18 +532,21 @@
"screen_create_poll_remove_accessibility_label" = "Remove %1$@"; "screen_create_poll_remove_accessibility_label" = "Remove %1$@";
"screen_create_poll_settings_section_title" = "Settings"; "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_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_new_space_title" = "New space";
"screen_create_room_public_option_short_description" = "Anyone can join."; "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_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_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_footer" = "You’ll need an address in order to make it visible in the public directory.";
"screen_create_room_room_address_section_title" = "Room address"; "screen_create_room_room_address_section_title" = "Address";
"screen_create_room_room_visibility_section_title" = "Room visibility"; "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_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_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."; "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_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_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_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_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_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."; "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_section_footer" = "Once enabled, encryption cannot be disabled.";
"screen_security_and_privacy_encryption_toggle_title" = "Enable end-to-end encryption"; "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_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_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_description" = "Only invited people can join.";
"screen_security_and_privacy_room_access_invite_only_option_title" = "Invite only"; "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_profile_row_title" = "Profile";
"screen_room_details_security_and_privacy_title" = "Security & privacy"; "screen_room_details_security_and_privacy_title" = "Security & privacy";
"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "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_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_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_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"; "screen_space_settings_leave_space" = "Leave space";
@@ -1447,8 +1450,6 @@
"screen_create_account_title" = "Create account"; "screen_create_account_title" = "Create account";
"screen_create_poll_cancel_confirmation_content_ios" = "Your changes won’t be saved"; "screen_create_poll_cancel_confirmation_content_ios" = "Your changes won’t be saved";
"screen_create_room_add_people_title" = "Invite people"; "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_deactivate_account_title" = "Deactivate account";
"screen_dm_details_block_alert_action" = "Block"; "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."; "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_remove_accessibility_label" = "Remove %1$@";
"screen_create_poll_settings_section_title" = "Settings"; "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_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_new_space_title" = "New space";
"screen_create_room_public_option_short_description" = "Anyone can join."; "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_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_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_footer" = "You’ll need an address in order to make it visible in the public directory.";
"screen_create_room_room_address_section_title" = "Room address"; "screen_create_room_room_address_section_title" = "Address";
"screen_create_room_room_visibility_section_title" = "Room visibility"; "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_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_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."; "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_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_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_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_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_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."; "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_section_footer" = "Once enabled, encryption cannot be disabled.";
"screen_security_and_privacy_encryption_toggle_title" = "Enable end-to-end encryption"; "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_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_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_description" = "Only invited people can join.";
"screen_security_and_privacy_room_access_invite_only_option_title" = "Invite only"; "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_profile_row_title" = "Profile";
"screen_room_details_security_and_privacy_title" = "Security & privacy"; "screen_room_details_security_and_privacy_title" = "Security & privacy";
"screen_roomlist_knock_event_sent_description" = "Request to join sent"; "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_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_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_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"; "screen_space_settings_leave_space" = "Leave space";
@@ -1447,8 +1450,6 @@
"screen_create_account_title" = "Create account"; "screen_create_account_title" = "Create account";
"screen_create_poll_cancel_confirmation_content_ios" = "Your changes won’t be saved"; "screen_create_poll_cancel_confirmation_content_ios" = "Your changes won’t be saved";
"screen_create_room_add_people_title" = "Invite people"; "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_deactivate_account_title" = "Deactivate account";
"screen_dm_details_block_alert_action" = "Block"; "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."; "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) { private func startStartChatFlow(animated: Bool) {
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = StartChatFlowCoordinator(userDiscoveryService: UserDiscoveryService(clientProxy: userSession.clientProxy), let coordinator = StartChatFlowCoordinator(isSpace: false,
userDiscoveryService: UserDiscoveryService(clientProxy: userSession.clientProxy),
navigationStackCoordinator: navigationStackCoordinator, navigationStackCoordinator: navigationStackCoordinator,
flowParameters: flowParameters) flowParameters: flowParameters)
@@ -16,6 +16,7 @@ enum StartChatFlowCoordinatorAction {
} }
class StartChatFlowCoordinator: FlowCoordinatorProtocol { class StartChatFlowCoordinator: FlowCoordinatorProtocol {
private let isSpace: Bool
private let userDiscoveryService: UserDiscoveryServiceProtocol private let userDiscoveryService: UserDiscoveryServiceProtocol
private let navigationStackCoordinator: NavigationStackCoordinator private let navigationStackCoordinator: NavigationStackCoordinator
@@ -63,9 +64,11 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
init(userDiscoveryService: UserDiscoveryServiceProtocol, init(isSpace: Bool,
userDiscoveryService: UserDiscoveryServiceProtocol,
navigationStackCoordinator: NavigationStackCoordinator, navigationStackCoordinator: NavigationStackCoordinator,
flowParameters: CommonFlowParameters) { flowParameters: CommonFlowParameters) {
self.isSpace = isSpace
self.userDiscoveryService = userDiscoveryService self.userDiscoveryService = userDiscoveryService
self.navigationStackCoordinator = navigationStackCoordinator self.navigationStackCoordinator = navigationStackCoordinator
@@ -168,7 +171,8 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
} }
private func presentCreateRoomScreen() { private func presentCreateRoomScreen() {
let createParameters = CreateRoomScreenCoordinatorParameters(userSession: flowParameters.userSession, let createParameters = CreateRoomScreenCoordinatorParameters(isSpace: isSpace,
userSession: flowParameters.userSession,
userIndicatorController: flowParameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
appSettings: flowParameters.appSettings, appSettings: flowParameters.appSettings,
analytics: flowParameters.analytics) 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") } 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. /// 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") } 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 /// New space
internal static var screenCreateRoomNewSpaceTitle: String { return L10n.tr("Localizable", "screen_create_room_new_space_title") } internal static var screenCreateRoomNewSpaceTitle: String { return L10n.tr("Localizable", "screen_create_room_new_space_title") }
/// Only people invited can join. /// 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") } internal static var screenCreateRoomPublicOptionShortDescription: String { return L10n.tr("Localizable", "screen_create_room_public_option_short_description") }
/// Public /// Public
internal static var screenCreateRoomPublicOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_public_option_title") } 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. /// 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") } 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") } 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 /// Who has access
internal static var screenCreateRoomRoomAccessSectionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_title") } 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") } 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") } 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 /// Room visibility
internal static var screenCreateRoomRoomVisibilitySectionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_visibility_section_title") } 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) /// Topic (optional)
internal static var screenCreateRoomTopicLabel: String { return L10n.tr("Localizable", "screen_create_room_topic_label") } 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. /// 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") } internal static var screenDeactivateAccountConfirmationDialogContent: String { return L10n.tr("Localizable", "screen_deactivate_account_confirmation_dialog_content") }
/// Delete all my messages /// Delete all my messages
+1 -1
View File
@@ -73,7 +73,7 @@ extension ClientProxyMock {
canDeactivateAccount = false canDeactivateAccount = false
directRoomForUserIDReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) directRoomForUserIDReturnValue = .failure(.sdkError(ClientProxyMockError.generic))
createDirectRoomWithExpectedRoomNameReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) createDirectRoomWithExpectedRoomNameReturnValue = .failure(.sdkError(ClientProxyMockError.generic))
createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReturnValue = .failure(.sdkError(ClientProxyMockError.generic))
canJoinRoomWithReturnValue = true canJoinRoomWithReturnValue = true
joinRoomViaClosure = { roomID, _ in joinRoomViaClosure = { roomID, _ in
configuration.overrides.joinedRoomIDs.insert(roomID) configuration.overrides.joinedRoomIDs.insert(roomID)
@@ -2847,15 +2847,15 @@ class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
} }
//MARK: - createRoom //MARK: - createRoom
var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount = 0 var createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount = 0
var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartCallsCount: Int { var createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartCallsCount: Int {
get { get {
if Thread.isMainThread { if Thread.isMainThread {
return createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount return createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount
} else { } else {
var returnValue: Int? = nil var returnValue: Int? = nil
DispatchQueue.main.sync { DispatchQueue.main.sync {
returnValue = createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount returnValue = createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount
} }
return returnValue! return returnValue!
@@ -2863,29 +2863,29 @@ class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
} }
set { set {
if Thread.isMainThread { if Thread.isMainThread {
createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount = newValue createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount = newValue
} else { } else {
DispatchQueue.main.sync { DispatchQueue.main.sync {
createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount = newValue createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingCallsCount = newValue
} }
} }
} }
} }
var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartCalled: Bool { var createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartCalled: Bool {
return createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartCallsCount > 0 return createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartCallsCount > 0
} }
var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReceivedArguments: (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 createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReceivedInvocations: [(name: String, topic: String?, isRoomPrivate: Bool, isKnockingOnly: 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 createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingReturnValue: Result<String, ClientProxyError>!
var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReturnValue: Result<String, ClientProxyError>! { var createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReturnValue: Result<String, ClientProxyError>! {
get { get {
if Thread.isMainThread { if Thread.isMainThread {
return createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingReturnValue return createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingReturnValue
} else { } else {
var returnValue: Result<String, ClientProxyError>? = nil var returnValue: Result<String, ClientProxyError>? = nil
DispatchQueue.main.sync { DispatchQueue.main.sync {
returnValue = createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingReturnValue returnValue = createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingReturnValue
} }
return returnValue! return returnValue!
@@ -2893,26 +2893,26 @@ class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
} }
set { set {
if Thread.isMainThread { if Thread.isMainThread {
createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartUnderlyingReturnValue = newValue createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartUnderlyingReturnValue = newValue
} else { } else {
DispatchQueue.main.sync { 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> { func createRoom(name: String, topic: String?, accessType: CreateRoomAccessType, isSpace: Bool, userIDs: [String], avatarURL: URL?, aliasLocalPart: String?) async -> Result<String, ClientProxyError> {
createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartCallsCount += 1 createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartCallsCount += 1
createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReceivedArguments = (name: name, topic: topic, isRoomPrivate: isRoomPrivate, isKnockingOnly: isKnockingOnly, userIDs: userIDs, avatarURL: avatarURL, aliasLocalPart: aliasLocalPart) createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments = (name: name, topic: topic, accessType: accessType, isSpace: isSpace, userIDs: userIDs, avatarURL: avatarURL, aliasLocalPart: aliasLocalPart)
DispatchQueue.main.async { 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 { if let createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartClosure = createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartClosure {
return await createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartClosure(name, topic, isRoomPrivate, isKnockingOnly, userIDs, avatarURL, aliasLocalPart) return await createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartClosure(name, topic, accessType, isSpace, userIDs, avatarURL, aliasLocalPart)
} else { } else {
return createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReturnValue return createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReturnValue
} }
} }
//MARK: - joinRoom //MARK: - joinRoom
@@ -10,6 +10,7 @@ import Combine
import SwiftUI import SwiftUI
struct CreateRoomScreenCoordinatorParameters { struct CreateRoomScreenCoordinatorParameters {
let isSpace: Bool
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let appSettings: AppSettings let appSettings: AppSettings
@@ -31,7 +32,8 @@ final class CreateRoomScreenCoordinator: CoordinatorProtocol {
} }
init(parameters: CreateRoomScreenCoordinatorParameters) { init(parameters: CreateRoomScreenCoordinatorParameters) {
viewModel = CreateRoomScreenViewModel(userSession: parameters.userSession, viewModel = CreateRoomScreenViewModel(isSpace: parameters.isSpace,
userSession: parameters.userSession,
analytics: parameters.analytics, analytics: parameters.analytics,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: parameters.userIndicatorController,
appSettings: parameters.appSettings) appSettings: parameters.appSettings)
@@ -23,12 +23,13 @@ enum CreateRoomScreenViewModelAction {
} }
struct CreateRoomScreenViewState: BindableState { struct CreateRoomScreenViewState: BindableState {
let isSpace: Bool
var roomName: String var roomName: String
let serverName: String let serverName: String
let isKnockingFeatureEnabled: Bool let isKnockingFeatureEnabled: Bool
var aliasLocalPart: String var aliasLocalPart: String
var bindings: CreateRoomScreenViewStateBindings var bindings: CreateRoomScreenViewStateBindings
var avatarURL: URL? var avatarMediaInfo: MediaInfo?
var canCreateRoom: Bool { var canCreateRoom: Bool {
!roomName.isEmpty && aliasErrors.isEmpty !roomName.isEmpty && aliasErrors.isEmpty
} }
@@ -43,12 +44,19 @@ struct CreateRoomScreenViewState: BindableState {
nil nil
} }
} }
var availableAccessTypes: [CreateRoomAccessType] {
var availableTypes = CreateRoomAccessType.allCases
if !isKnockingFeatureEnabled {
availableTypes.removeAll { $0 == .askToJoin }
}
return availableTypes
}
} }
struct CreateRoomScreenViewStateBindings { struct CreateRoomScreenViewStateBindings {
var roomTopic: String var roomTopic: String
var isRoomPrivate: Bool var selectedAccessType: CreateRoomAccessType
var isKnockingOnly: Bool
var showAttachmentConfirmationDialog = false var showAttachmentConfirmationDialog = false
/// Information describing the currently displayed alert. /// Information describing the currently displayed alert.
@@ -13,17 +13,7 @@ import SwiftUI
typealias CreateRoomScreenViewModelType = StateStoreViewModel<CreateRoomScreenViewState, CreateRoomScreenViewAction> typealias CreateRoomScreenViewModelType = StateStoreViewModel<CreateRoomScreenViewState, CreateRoomScreenViewAction>
class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreenViewModelProtocol { 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 let userSession: UserSessionProtocol
private var parameters: Parameters
private let mediaUploadingPreprocessor: MediaUploadingPreprocessor private let mediaUploadingPreprocessor: MediaUploadingPreprocessor
private let analytics: AnalyticsService private let analytics: AnalyticsService
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
@@ -36,25 +26,24 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
init(userSession: UserSessionProtocol, init(isSpace: Bool,
initialParameters: Parameters = .init(), userSession: UserSessionProtocol,
analytics: AnalyticsService, analytics: AnalyticsService,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
appSettings: AppSettings) { appSettings: AppSettings) {
self.userSession = userSession self.userSession = userSession
parameters = initialParameters
mediaUploadingPreprocessor = MediaUploadingPreprocessor(appSettings: appSettings) mediaUploadingPreprocessor = MediaUploadingPreprocessor(appSettings: appSettings)
self.analytics = analytics self.analytics = analytics
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
let bindings = CreateRoomScreenViewStateBindings(roomTopic: parameters.topic, let bindings = CreateRoomScreenViewStateBindings(roomTopic: "",
isRoomPrivate: parameters.isRoomPrivate, selectedAccessType: .private)
isKnockingOnly: appSettings.knockingEnabled ? parameters.isKnockingOnly : false)
super.init(initialViewState: CreateRoomScreenViewState(roomName: parameters.name, super.init(initialViewState: CreateRoomScreenViewState(isSpace: isSpace,
roomName: "",
serverName: userSession.clientProxy.userIDServerName ?? "", serverName: userSession.clientProxy.userIDServerName ?? "",
isKnockingFeatureEnabled: appSettings.knockingEnabled, isKnockingFeatureEnabled: appSettings.knockingEnabled,
aliasLocalPart: parameters.aliasLocalPart ?? roomAliasNameFromRoomDisplayName(roomName: parameters.name), aliasLocalPart: roomAliasNameFromRoomDisplayName(roomName: ""),
bindings: bindings), bindings: bindings),
mediaProvider: userSession.mediaProvider) mediaProvider: userSession.mediaProvider)
@@ -72,8 +61,7 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
case .displayMediaPicker: case .displayMediaPicker:
actionsSubject.send(.displayMediaPicker) actionsSubject.send(.displayMediaPicker)
case .removeImage: case .removeImage:
parameters.avatarImageMedia = nil state.avatarMediaInfo = nil
state.avatarURL = nil
case .updateAliasLocalPart(let aliasLocalPart): case .updateAliasLocalPart(let aliasLocalPart):
state.aliasLocalPart = aliasLocalPart.lowercased() state.aliasLocalPart = aliasLocalPart.lowercased()
// If this has been called this means that the user wants a custom address not necessarily reflecting the name // 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() let mediaInfo = try await mediaUploadingPreprocessor.processMedia(at: fileURL, maxUploadSize: maxUploadSize).get()
switch mediaInfo { switch mediaInfo {
case .image(_, let thumbnailURL, _): case .image:
parameters.avatarImageMedia = mediaInfo state.avatarMediaInfo = mediaInfo
state.avatarURL = thumbnailURL
default: default:
break 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 // Reset the state related to public rooms if the user choses the room to be empty
context.$viewState context.$viewState
.dropFirst() .dropFirst()
.map(\.bindings.isRoomPrivate) .map(\.bindings.selectedAccessType)
.removeDuplicates() .removeDuplicates()
.filter { $0 } .filter(\.isPrivate)
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [weak self] _ in .sink { [weak self] _ in
guard let self else { return } guard let self else { return }
state.bindings.isKnockingOnly = false
state.aliasErrors = [] state.aliasErrors = []
state.aliasLocalPart = roomAliasNameFromRoomDisplayName(roomName: state.roomName) state.aliasLocalPart = roomAliasNameFromRoomDisplayName(roomName: state.roomName)
syncNameAndAlias = true syncNameAndAlias = true
} }
.store(in: &cancellables) .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 context.$viewState
.map(\.aliasLocalPart) .map(\.aliasLocalPart)
.removeDuplicates() .removeDuplicates()
@@ -160,8 +131,7 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
return return
} }
guard state.isKnockingFeatureEnabled, guard !state.bindings.selectedAccessType.isPrivate,
!state.bindings.isRoomPrivate,
let canonicalAlias = String.makeCanonicalAlias(aliasLocalPart: aliasLocalPart, let canonicalAlias = String.makeCanonicalAlias(aliasLocalPart: aliasLocalPart,
serverName: state.serverName) else { serverName: state.serverName) else {
// While is empty or private room we don't change or display the error // 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) .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 { private func createRoom() async {
defer { defer {
hideLoadingIndicator() hideLoadingIndicator()
} }
showLoadingIndicator() 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 // Better to double check the errors also when trying to create the room
if state.isKnockingFeatureEnabled, !parameters.isRoomPrivate { if !state.bindings.selectedAccessType.isPrivate {
guard let canonicalAlias = String.makeCanonicalAlias(aliasLocalPart: parameters.aliasLocalPart, guard let canonicalAlias = String.makeCanonicalAlias(aliasLocalPart: state.aliasLocalPart,
serverName: state.serverName), serverName: state.serverName),
isRoomAliasFormatValid(alias: canonicalAlias) else { isRoomAliasFormatValid(alias: canonicalAlias) else {
state.aliasErrors = [.invalidSymbols] state.aliasErrors = [.invalidSymbols]
@@ -238,7 +193,7 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
} }
let avatarURL: URL? let avatarURL: URL?
if let media = parameters.avatarImageMedia { if let media = state.avatarMediaInfo {
switch await userSession.clientProxy.uploadMedia(media) { switch await userSession.clientProxy.uploadMedia(media) {
case .success(let url): case .success(let url):
avatarURL = URL(string: url) avatarURL = URL(string: url)
@@ -263,14 +218,13 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
avatarURL = nil avatarURL = nil
} }
switch await userSession.clientProxy.createRoom(name: parameters.name, switch await userSession.clientProxy.createRoom(name: state.roomName,
topic: parameters.topic.isBlank ? nil : parameters.topic, topic: state.bindings.roomTopic.isBlank ? nil : state.bindings.roomTopic,
isRoomPrivate: parameters.isRoomPrivate, accessType: state.bindings.selectedAccessType,
// As of right now we don't want to make private rooms with the knock rule isSpace: state.isSpace,
isKnockingOnly: parameters.isRoomPrivate ? false : parameters.isKnockingOnly,
userIDs: [], // The invite users screen is shown next so we don't need to invite anyone right now. userIDs: [], // The invite users screen is shown next so we don't need to invite anyone right now.
avatarURL: avatarURL, avatarURL: avatarURL,
aliasLocalPart: parameters.isRoomPrivate ? nil : parameters.aliasLocalPart) { aliasLocalPart: state.bindings.selectedAccessType.isPrivate ? nil : state.aliasLocalPart) {
case .success(let roomID): case .success(let roomID):
guard case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(roomID) else { guard case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(roomID) else {
state.bindings.alertInfo = AlertInfo(id: .failedCreatingRoom, state.bindings.alertInfo = AlertInfo(id: .failedCreatingRoom,
@@ -39,17 +39,15 @@ struct CreateRoomScreen: View {
Form { Form {
roomSection roomSection
topicSection topicSection
securitySection roomAccessSection
if context.viewState.isKnockingFeatureEnabled, if !context.selectedAccessType.isPrivate {
!context.isRoomPrivate {
roomAccessSection
roomAliasSection roomAliasSection
} }
} }
.compoundList() .compoundList()
.track(screen: .CreateRoom) .track(screen: .CreateRoom)
.scrollDismissesKeyboard(.immediately) .scrollDismissesKeyboard(.immediately)
.navigationTitle(L10n.screenCreateRoomTitle) .navigationTitle(context.viewState.isSpace ? L10n.screenCreateRoomNewSpaceTitle : L10n.screenCreateRoomNewRoomTitle)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.toolbar { toolbar } .toolbar { toolbar }
.alert(item: $context.alertInfo) .alert(item: $context.alertInfo)
@@ -60,15 +58,20 @@ struct CreateRoomScreen: View {
Section { Section {
HStack(alignment: .center, spacing: 16) { HStack(alignment: .center, spacing: 16) {
roomAvatarButton roomAvatarButton
let nameLabel = if #available(iOS 26, *) {
L10n.commonName
} else {
L10n.commonName.uppercased()
}
VStack(alignment: .leading, spacing: 8) { VStack(alignment: .leading, spacing: 8) {
Text(L10n.screenCreateRoomRoomNameLabel.uppercased()) Text(nameLabel)
.padding(.leading, ListRowPadding.horizontal) .padding(.leading, ListRowPadding.horizontal)
.compoundListSectionHeader() .compoundListSectionHeader()
TextField(L10n.screenCreateRoomRoomNameLabel, TextField(L10n.commonName,
text: roomNameBinding, text: roomNameBinding,
prompt: Text(L10n.commonRoomNamePlaceholder).foregroundColor(.compound.textSecondary), prompt: Text(L10n.screenCreateRoomNamePlaceholder).foregroundColor(.compound.textSecondary),
axis: .horizontal) axis: .horizontal)
.font(.compound.bodyLG) .font(.compound.bodyLG)
.foregroundStyle(.compound.textPrimary) .foregroundStyle(.compound.textPrimary)
@@ -90,7 +93,7 @@ struct CreateRoomScreen: View {
focus = nil focus = nil
context.showAttachmentConfirmationDialog = true context.showAttachmentConfirmationDialog = true
} label: { } label: {
if let url = context.viewState.avatarURL { if let url = context.viewState.avatarMediaInfo?.thumbnailURL {
AsyncImage(url: url) { image in AsyncImage(url: url) { image in
image image
.resizable() .resizable()
@@ -118,7 +121,7 @@ struct CreateRoomScreen: View {
} }
.accessibilityIdentifier(A11yIdentifiers.createRoomScreen.mediaPicker) .accessibilityIdentifier(A11yIdentifiers.createRoomScreen.mediaPicker)
if context.viewState.avatarURL != nil { if context.viewState.avatarMediaInfo?.thumbnailURL != nil {
Button(L10n.actionRemove, role: .destructive) { Button(L10n.actionRemove, role: .destructive) {
context.send(viewAction: .removeImage) context.send(viewAction: .removeImage)
} }
@@ -128,7 +131,7 @@ struct CreateRoomScreen: View {
private var topicSection: some View { private var topicSection: some View {
Section { Section {
ListRow(label: .plain(title: L10n.commonTopicPlaceholder), ListRow(label: .plain(title: L10n.screenCreateRoomTopicPlaceholder),
kind: .textField(text: $context.roomTopic, axis: .vertical)) kind: .textField(text: $context.roomTopic, axis: .vertical))
.lineLimit(3, reservesSpace: false) .lineLimit(3, reservesSpace: false)
.focused($focus, equals: .topic) .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 { private var roomAccessSection: some View {
Section { Section {
ListRow(label: .plain(title: L10n.screenCreateRoomRoomAccessSectionAnyoneOptionTitle, ForEach(context.viewState.availableAccessTypes, id: \.self) { accessType in
description: L10n.screenCreateRoomRoomAccessSectionAnyoneOptionDescription), CreateRoomAccessRow(access: accessType,
kind: .selection(isSelected: !context.isKnockingOnly) { context.isKnockingOnly = false }) isSelected: context.selectedAccessType == accessType) {
ListRow(label: .plain(title: L10n.screenCreateRoomRoomAccessSectionKnockingOptionTitle, context.selectedAccessType = accessType
description: L10n.screenCreateRoomRoomAccessSectionKnockingOptionDescription), }
kind: .selection(isSelected: context.isKnockingOnly) { context.isKnockingOnly = true }) }
} header: { } header: {
Text(L10n.screenCreateRoomRoomAccessSectionHeader) Text(L10n.screenCreateRoomRoomAccessSectionTitle)
.compoundListSectionHeader() .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 // MARK: - Previews
struct CreateRoom_Previews: PreviewProvider, TestablePreview { struct CreateRoom_Previews: PreviewProvider, TestablePreview {
static let viewModel = { static let viewModel = {
AppSettings.resetAllSettings()
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com")))) let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com"))))
return CreateRoomScreenViewModel(userSession: userSession, return CreateRoomScreenViewModel(isSpace: false,
initialParameters: .init(), 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, analytics: ServiceLocator.shared.analytics,
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: ServiceLocator.shared.settings) appSettings: ServiceLocator.shared.settings)
}() }()
static let publicRoomViewModel = { static let publicRoomViewModel = {
AppSettings.resetAllSettings()
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com")))) let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com"))))
ServiceLocator.shared.settings.knockingEnabled = true let viewModel = CreateRoomScreenViewModel(isSpace: false,
return CreateRoomScreenViewModel(userSession: userSession, userSession: userSession,
initialParameters: .init(isRoomPrivate: false), analytics: ServiceLocator.shared.analytics,
analytics: ServiceLocator.shared.analytics, userIndicatorController: UserIndicatorControllerMock(),
userIndicatorController: UserIndicatorControllerMock(), appSettings: ServiceLocator.shared.settings)
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 = { static let publicRoomInvalidAliasViewModel = {
AppSettings.resetAllSettings()
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com")))) let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com"))))
ServiceLocator.shared.settings.knockingEnabled = true let viewModel = CreateRoomScreenViewModel(isSpace: false,
return CreateRoomScreenViewModel(userSession: userSession, userSession: userSession,
initialParameters: .init(isRoomPrivate: false, aliasLocalPart: "#:"), analytics: ServiceLocator.shared.analytics,
analytics: ServiceLocator.shared.analytics, userIndicatorController: UserIndicatorControllerMock(),
userIndicatorController: UserIndicatorControllerMock(), appSettings: ServiceLocator.shared.settings)
appSettings: ServiceLocator.shared.settings) viewModel.context.selectedAccessType = .public
viewModel.context.send(viewAction: .updateAliasLocalPart("#:"))
return viewModel
}() }()
static let publicRoomExistingAliasViewModel = { static let publicRoomExistingAliasViewModel = {
AppSettings.resetAllSettings()
let clientProxy = ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com")) let clientProxy = ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com"))
clientProxy.isAliasAvailableReturnValue = .success(false) clientProxy.isAliasAvailableReturnValue = .success(false)
let userSession = UserSessionMock(.init(clientProxy: clientProxy)) let userSession = UserSessionMock(.init(clientProxy: clientProxy))
ServiceLocator.shared.settings.knockingEnabled = true let viewModel = CreateRoomScreenViewModel(isSpace: false,
return CreateRoomScreenViewModel(userSession: userSession, userSession: userSession,
initialParameters: .init(isRoomPrivate: false, aliasLocalPart: "existing"), analytics: ServiceLocator.shared.analytics,
analytics: ServiceLocator.shared.analytics, userIndicatorController: UserIndicatorControllerMock(),
userIndicatorController: UserIndicatorControllerMock(), appSettings: ServiceLocator.shared.settings)
appSettings: ServiceLocator.shared.settings) viewModel.context.selectedAccessType = .public
viewModel.context.send(viewAction: .updateAliasLocalPart("existing"))
return viewModel
}() }()
static var previews: some View { static var previews: some View {
@@ -257,11 +323,21 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
} }
.previewDisplayName("Create Room") .previewDisplayName("Create Room")
NavigationStack {
CreateRoomScreen(context: spaceViewModel.context)
}
.previewDisplayName("Create Space")
NavigationStack { NavigationStack {
CreateRoomScreen(context: publicRoomViewModel.context) CreateRoomScreen(context: publicRoomViewModel.context)
} }
.previewDisplayName("Create Public Room") .previewDisplayName("Create Public Room")
NavigationStack {
CreateRoomScreen(context: askToJoinViewModel.context)
}
.previewDisplayName("Create Knockable Room")
NavigationStack { NavigationStack {
CreateRoomScreen(context: publicRoomInvalidAliasViewModel.context) CreateRoomScreen(context: publicRoomInvalidAliasViewModel.context)
} }
@@ -48,13 +48,15 @@ struct SecurityAndPrivacyScreen: View {
Section { Section {
ListRow(label: .default(title: L10n.screenSecurityAndPrivacyRoomAccessAnyoneOptionTitle, ListRow(label: .default(title: L10n.screenSecurityAndPrivacyRoomAccessAnyoneOptionTitle,
description: L10n.screenSecurityAndPrivacyRoomAccessAnyoneOptionDescription, description: L10n.screenSecurityAndPrivacyRoomAccessAnyoneOptionDescription,
icon: \.public), icon: \.public,
iconAlignment: .top),
kind: .selection(isSelected: context.desiredSettings.accessType == .anyone) { context.desiredSettings.accessType = .anyone }) kind: .selection(isSelected: context.desiredSettings.accessType == .anyone) { context.desiredSettings.accessType = .anyone })
if context.viewState.isSpaceMembersOptionAvailable { if context.viewState.isSpaceMembersOptionAvailable {
ListRow(label: .default(title: L10n.screenSecurityAndPrivacyRoomAccessSpaceMembersOptionTitle, ListRow(label: .default(title: L10n.screenSecurityAndPrivacyRoomAccessSpaceMembersOptionTitle,
description: context.viewState.spaceMembersDescription, description: context.viewState.spaceMembersDescription,
icon: \.space), icon: \.space,
iconAlignment: .top),
kind: .selection(isSelected: context.desiredSettings.accessType.isSpaceMembers) { kind: .selection(isSelected: context.desiredSettings.accessType.isSpaceMembers) {
context.send(viewAction: .selectedSpaceMembersAccess) context.send(viewAction: .selectedSpaceMembersAccess)
}) })
@@ -74,7 +76,8 @@ struct SecurityAndPrivacyScreen: View {
ListRow(label: .default(title: L10n.screenSecurityAndPrivacyRoomAccessInviteOnlyOptionTitle, ListRow(label: .default(title: L10n.screenSecurityAndPrivacyRoomAccessInviteOnlyOptionTitle,
description: L10n.screenSecurityAndPrivacyRoomAccessInviteOnlyOptionDescription, description: L10n.screenSecurityAndPrivacyRoomAccessInviteOnlyOptionDescription,
icon: \.lock), icon: \.lock,
iconAlignment: .top),
kind: .selection(isSelected: context.desiredSettings.accessType == .inviteOnly) { context.desiredSettings.accessType = .inviteOnly }) kind: .selection(isSelected: context.desiredSettings.accessType == .inviteOnly) { context.desiredSettings.accessType = .inviteOnly })
} header: { } header: {
Text(L10n.screenSecurityAndPrivacyRoomAccessSectionHeader) Text(L10n.screenSecurityAndPrivacyRoomAccessSectionHeader)
@@ -94,7 +97,8 @@ struct SecurityAndPrivacyScreen: View {
private var askToJoinOption: some View { private var askToJoinOption: some View {
ListRow(label: .default(title: L10n.screenSecurityAndPrivacyAskToJoinOptionTitle, ListRow(label: .default(title: L10n.screenSecurityAndPrivacyAskToJoinOptionTitle,
description: L10n.screenSecurityAndPrivacyAskToJoinOptionDescription, description: L10n.screenSecurityAndPrivacyAskToJoinOptionDescription,
icon: \.userAdd), icon: \.userAdd,
iconAlignment: .top),
kind: .selection(isSelected: context.desiredSettings.accessType == .askToJoin) { context.desiredSettings.accessType = .askToJoin }) kind: .selection(isSelected: context.desiredSettings.accessType == .askToJoin) { context.desiredSettings.accessType = .askToJoin })
.disabled(!context.viewState.isKnockingEnabled) .disabled(!context.viewState.isKnockingEnabled)
} }
@@ -102,7 +106,8 @@ struct SecurityAndPrivacyScreen: View {
private var askToJoinWithSpaceMembersOption: some View { private var askToJoinWithSpaceMembersOption: some View {
ListRow(label: .default(title: L10n.screenSecurityAndPrivacyAskToJoinOptionTitle, ListRow(label: .default(title: L10n.screenSecurityAndPrivacyAskToJoinOptionTitle,
description: context.viewState.askToJoinWithSpaceMembersDescription, description: context.viewState.askToJoinWithSpaceMembersDescription,
icon: \.userAdd), icon: \.userAdd,
iconAlignment: .top),
kind: .selection(isSelected: context.desiredSettings.accessType.isAskToJoinWithSpaceMembers) { context.send(viewAction: .selectedAskToJoinWithSpaceMembersAccess) }) kind: .selection(isSelected: context.desiredSettings.accessType.isAskToJoinWithSpaceMembers) { context.send(viewAction: .selectedAskToJoinWithSpaceMembersAccess) })
.disabled(!context.viewState.isAskToJoinWithSpaceMembersOptionSelectable) .disabled(!context.viewState.isAskToJoinWithSpaceMembersOptionSelectable)
} }
@@ -474,25 +474,26 @@ class ClientProxy: ClientProxyProtocol {
func createRoom(name: String, func createRoom(name: String,
topic: String?, topic: String?,
isRoomPrivate: Bool, accessType: CreateRoomAccessType,
isKnockingOnly: Bool, isSpace: Bool,
userIDs: [String], userIDs: [String],
avatarURL: URL?, avatarURL: URL?,
aliasLocalPart: String?) async -> Result<String, ClientProxyError> { aliasLocalPart: String?) async -> Result<String, ClientProxyError> {
do { do {
let parameters = CreateRoomParameters(name: name, let parameters = CreateRoomParameters(name: name,
topic: topic, topic: topic,
isEncrypted: isRoomPrivate, isEncrypted: accessType.isEncrypted,
isDirect: false, isDirect: false,
visibility: isRoomPrivate ? .private : .public, visibility: accessType.visibility,
preset: isRoomPrivate ? .privateChat : .publicChat, preset: accessType.preset,
invite: userIDs, invite: userIDs,
avatar: avatarURL?.absoluteString, avatar: avatarURL?.absoluteString,
powerLevelContentOverride: isKnockingOnly ? Self.knockingRoomCreationPowerLevelOverrides : Self.roomCreationPowerLevelOverrides, powerLevelContentOverride: accessType == .askToJoin ? Self.knockingRoomCreationPowerLevelOverrides : Self.roomCreationPowerLevelOverrides,
joinRuleOverride: isKnockingOnly ? .knock : nil, joinRuleOverride: accessType.joinRuleOverride,
historyVisibilityOverride: isRoomPrivate ? .invited : nil, historyVisibilityOverride: accessType.historyVisibilityOverride,
// This is an FFI naming mistake, what is required is the `aliasLocalPart` not the whole alias // 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) let roomID = try await client.createRoom(request: parameters)
await waitForRoomToSync(roomID: roomID) 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 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 /// 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: /// using the Matrix Rust SDK, more info here:
/// https://github.com/matrix-org/sygnal /// https://github.com/matrix-org/sygnal
@@ -156,8 +171,8 @@ protocol ClientProxyProtocol: AnyObject {
func createRoom(name: String, func createRoom(name: String,
topic: String?, topic: String?,
isRoomPrivate: Bool, accessType: CreateRoomAccessType,
isKnockingOnly: Bool, isSpace: Bool,
userIDs: [String], userIDs: [String],
avatarURL: URL?, avatarURL: URL?,
aliasLocalPart: String?) async -> Result<String, ClientProxyError> aliasLocalPart: String?) async -> Result<String, ClientProxyError>
@@ -651,14 +651,16 @@ class MockScreen: Identifiable {
return navigationStackCoordinator return navigationStackCoordinator
case .startChatFlow: case .startChatFlow:
let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com")) 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: []))) } clientProxy.roomForIdentifierClosure = { roomID in .joined(JoinedRoomProxyMock(.init(id: roomID, members: []))) }
let userDiscoveryService = UserDiscoveryServiceMock() let userDiscoveryService = UserDiscoveryServiceMock()
userDiscoveryService.searchProfilesWithReturnValue = .success([.mockBob, .mockBobby]) userDiscoveryService.searchProfilesWithReturnValue = .success([.mockBob, .mockBobby])
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let flowCoordinator = StartChatFlowCoordinator(userDiscoveryService: userDiscoveryService, let flowCoordinator = StartChatFlowCoordinator(isSpace: false,
userDiscoveryService: userDiscoveryService,
navigationStackCoordinator: navigationStackCoordinator, navigationStackCoordinator: navigationStackCoordinator,
flowParameters: CommonFlowParameters(userSession: UserSessionMock(.init(clientProxy: clientProxy)), flowParameters: CommonFlowParameters(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
bugReportService: BugReportServiceMock(.init()), 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 version https://git-lfs.github.com/spec/v1
oid sha256:0f8d601c193367fe6e2fcbcafeea76da0481328d4c63d6c6f3a5106f9869cc0f oid sha256:c744f0ddc6f0e2cb2e2063cc0135d18a07f18d3a58314ee33651962295199225
size 186260 size 158006
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:fc25f9648a4c81cf21d0d420fb08825f96d6a54fc1eedc589c673866a67af784 oid sha256:301081a176b706092f008e01c9a195dd58c7dc28adc7a42d38ee5e4c168cd1a2
size 224957 size 189421
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:98c9a9ee2f1c0d78a24a5e096c11f72f2467ee22c66f9fa26b84272732eb339c oid sha256:7128f0417b2479525d2b35bfb4a501ac24690694f34841bc7ecf0d922741654d
size 121268 size 109846
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:33ac15d63ca2d46ba68e087e712442761bc40feecba310825e661b97b59cfba2 oid sha256:2a656ea4051f6a84282fca5f94978b199ec10df51dac04a7ad01c4ec55b6ae1f
size 144352 size 146993
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:9df32336b119eb58e4aa3a66280b944c6b54714509fab4d843fd16304d5292ab oid sha256:2c110232459827a0f641057ed1bfc6bfdb714134f3a658fa5acee9a9395bf62a
size 180274 size 142457
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:21fc629bec2b888ab69f895fbb724c862383c67e5c8bfc3702f2a82ed046b5c3 oid sha256:729a6515bc93eb02be4d04a60c07f3854875ab2404a282a5de8bd11ae9e86a89
size 219688 size 165595
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:f5b29aa43bf4f260ca92827715079a66a2a721329510bdadb6e732ad6bbc067b oid sha256:9d4b216831eb0595f1b3adbf0fade0d3db2e621d7922dbc647b62fc31ddc854f
size 114041 size 88801
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:5e00046d8a037d82931c163483cb96238476af973db9c36b66f6253b2dc94459 oid sha256:c3cfa55a1a89d1905c5c21d5aeadce37d8554487054c2abe6c7d39323e7359c4
size 141659 size 115037
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:4c67f6b214cc6d321d3566b5c02f3f8a226bfd0745cd5092f45c7c3e1a73fb32 oid sha256:ea061eb260fdeebd3d38a6f32c209345f45dfe3188bff5ebb4e5fefa26bd73e3
size 189590 size 161310
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:0018ab26fdd70f668853b04b10575aa018cb8dcb7cc3863350108354e0735121 oid sha256:cc8f86bff3d1377c3ca9cca775c998a736a90211d7bb4d03439aa788bdc60160
size 227012 size 197756
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:55dff7dd30c3bbe8ba5a4791ecd30bad37947f6209daf4ef2c8d0dcc154dc4b0 oid sha256:c58d9126bdf00d7b64ac59b740645d456ad06fe66ff67f0e805eb4b140f0adc1
size 119611 size 111712
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:33ac15d63ca2d46ba68e087e712442761bc40feecba310825e661b97b59cfba2 oid sha256:3cc77ead9f512f919377e076cf95cba2678a0eb3d7c79395dfcdd33a82d32d3f
size 144352 size 152160
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:fdc1e9766a4dde854f05b4b42c3fe6d9faf451813a0230b10dfc241b20038ad7 oid sha256:26726c399334387ad4f4f9603af729dd998d8aa40e31175def2b1719e3fe27ec
size 138315 size 124557
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:6f5c3ffd80810d57d9dceb6f64189b30cf5e60628b48e6968896dacfc53a0e17 oid sha256:6f0b7534290fc773b98633250f05b434ef83c5f8243dd891de5bc58902941e28
size 161485 size 142357
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:572a0649d2ba4efa3056ce34a822d4b1237891fc15a538f80ac51ff6948952cc oid sha256:37a460ee15e802c3a165053bba730a911518682570d0692c2f1228913936cb95
size 86254 size 72654
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:968186dc96ec4e01fe52ff68ef441f85f48ad2d776ade28d983e650ec4b585c8 oid sha256:8b1447dc8d94468cbb42821eea9bc4431ad6c26682a3ef3901d11d80bddaed9a
size 106420 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 version https://git-lfs.github.com/spec/v1
oid sha256:4a2874e02e5bae080c7f422b1359199f70f208d60735825416419882e8161b80 oid sha256:f3a736a4d1805184d597e7e8209d72208cdb942015ca7f5b119b9be551a2d13a
size 195179 size 195381
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:c90113d2a9b105c70446f58fa0e766b1cfdb83d4dae9b4c34efbb575b2cc5d3e oid sha256:29a5425aee883225d39030768f821cf098084beb7f084998ffb25f914254d793
size 233537 size 233810
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:f7aa48ef3727c3fcc42a3b85a55417c217e50a4e50546b1fcb5bf92bb76152e3 oid sha256:c6a7aff5bc662c961cef7b941387ee8fd55b382496d0225d63a7baf2a85b3f2b
size 132863 size 133082
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:c56c5e6bf983dfbcc5aba2b9474be0c2f6da8c8fd94ec37cf72d4d02e24e227e oid sha256:8ddfbe02c83239f4ea3c8b95eec2c6f508d835da6191ccd5170092eee12f630e
size 164583 size 164847
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ca2c5743874b7c404c06b23bf133fb77d5f61812b1d55c2d5170b968915694ac oid sha256:ddfbaeef5378191153110b43f9f48f21182159a8c75fb9234de6f8c9257bdcdf
size 211272 size 211601
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:7ebf814a7cb3d144e7c5172fc889114d41929dacf8068458b62152f832ff426d oid sha256:36b336fb2119ba7753703704fc6ec507c429a389a820548e0c131e4b81c49fa6
size 260403 size 260759
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:58da8eeaec2f85afc1ecf3e762c79d94008b5fe725ae72e00aaf965858ef6b7c oid sha256:44eee6824cf09e831336f8032fdbadbe023e47167edd16ca4332aab9a1e56887
size 147164 size 147504
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:b877cc4e12ac4afefb2ef0793f09919e9b81127a9eaa8cb221ebe2138b6b5a3f oid sha256:407dc0929ce569c6cc4f5f5163ab8c1eaa79f35784e6949f96010a6f761067d0
size 179833 size 180458
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:bf83cc61198e77aa658cc17d57dbc1019569b2552bb341b90034f7345939e588 oid sha256:2b2cd165d6aa5fced734eb4207aa7dd252e47fce6f240f50b9434c5984e8e5a2
size 198334 size 198692
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:afd52028661858843b63bc99f715bd91fc10d5fb22b8eee3117875afee29722b oid sha256:8da0db3f1929d2cdedb348146e751e5e2b29bb2a251e3777ecf227b06cbab0a5
size 248058 size 248426
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:cf7256d898a8329af1c1153250d1492e377021de91aacd23544fe83cbf16edb2 oid sha256:2aa7d0bef7669c074cc9167e681a5e16fe29103f948d7e0959261f3ad1c0bb1b
size 136320 size 136685
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:1a48162c23078a6eac6e3afdbe16b8045bb558247ce956a18f8b8a84eb73ad9e oid sha256:bd5ff993dde6c617ef07b96e9d5841b13707f8d875c4a4c08f027359e65e7f0d
size 170604 size 171197
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:200ee11a5dd56886be8381f1a129b67c250d47a6eaefaedf82127973bb4f8a16 oid sha256:40718c83b3c6ae61aba5871cd179673684f38d516bf22070d2d19d920be5c228
size 173112 size 173334
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:7d348350791ed25e7e5db970a8128dda7f94c41216fb0f46f70f6c8b13540c78 oid sha256:711acf1affdf3fb3ec7f80776d78669e7e6652aef359e2285645af4b73c4a851
size 213524 size 213700
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:614305ed71ccea6133304c2b4caa9130437eb6ca55c7f01aba34cd78e678cca2 oid sha256:c5fcdfd6cfb12d365e13d42d101da721104f8b92ac4c26750fc60edd4a11c4d7
size 118448 size 118619
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:40bfe29281241156f291ebe633b913768441de7fa93d45e9cc98017373fcc842 oid sha256:a6debe0c740afde8f727afdc6e84294fb6e5c793559924c2107c1b629894a2b4
size 176498 size 176819
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:19ee4c8b01d1b18b962971ae0b1b950279863d12e832b572e2635837ace8f569 oid sha256:3466617944665b46015917c82d4dc84b420e5c54b53403f55d9fe78962f61d56
size 148707 size 148793
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:801c65df001931f413d10652a0934564b750cb7f2888760fe3ac8dd69461f649 oid sha256:8d016464d8f6cd7017e84465531dc9622fb865e61b6dc6cd7f9a9538a76befb5
size 177970 size 178171
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:108fa3154405e527b4f83dfb15f2d7720b375c349c8d9de50c4984192839ef26 oid sha256:3914a0e0ed46c0d88bb1fc368239c518ae2cc5687a836d16b5dd4bc1d76faf3d
size 93016 size 93124
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:e23e9b6268efabf0082f700393f080c7ff520af0a77a5c59d526800fb8b0e1b7 oid sha256:7f9eb7cc54ffed0a534a71b9ab2538fe3706c5d44a241429fffe05a5d7c1b197
size 132768 size 132874
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:9af7a48f714bec7db946bdf6762507aca1cd5609eadcb025857f7f3a7bd33179 oid sha256:c448b9630b608cd0bc4ddae356b31d6b5983339ef001f86e25b33fd14dbe4853
size 198158 size 198214
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:15dd87267271389ad687a635154757aba54788a2d47a1a89857495b7ceb3e093 oid sha256:7a0a36c9c6c4e1d671a2d6b076b8eee9317ced03b3f42ce08e7e650b4fb34ff0
size 250229 size 250367
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:5ea2f6d3dfa555f7be8886086f8f9885e8a537599e5e130d8019e870b5018b2a oid sha256:a1713eacee2906eb96f6cca8e1f44eba015a28e4d83d074287d9509ebd958ba8
size 134575 size 134682
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:1ddc6ad69921913ef2f085c0cba06f5e731e727b36a8e9533825ee636315252a oid sha256:5a186f342d0fb66171e821d425f3c946abded3d27ec8a26f69c992f28f898ed1
size 169254 size 169406
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:c324010c3328fa225c4477f1bb27be733072e5d8cd3ce888db6220a6d3bda0e6 oid sha256:0d1035d5d115c21eda179a45a159aa41590ec6234042323db1aa979982dddab2
size 170487 size 170541
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:3aaeb4185e78b1e6c4417bb7d76afac8421589f70fac6c486841b9974e386f09 oid sha256:9857f9df82cf5938e55cd02442a0ecf781eca44623fe32e3735127410b2f652d
size 215913 size 216055
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:bbc7316079d963ff3b8bc1680ba8bdc8212985668552de2fca39a6c51d004ab0 oid sha256:4e05ee29eca1bb23ad9238b8fdc1bd73a84901d12b234aaa2f7175f15dca22ef
size 115669 size 115798
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:23fddbb25038c9fdc0bfa53d48e0093cd22f4bc02453d0c4e436a64d531eb388 oid sha256:8184ca500b01f72d8fb86e034a4b0c2acfc9bcb884570f4ca3e266b7bce79542
size 153794 size 153899
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:41045c5701b59ed3164a4999189cff0f74772e420e511fc4339f2c4b2933762b oid sha256:56168eb647c611eb0c214d3598e5d2518fa2933ed08f83d4ccd6c27be94ce1d1
size 151773 size 151831
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:8ce1a903be20de8485976cfcf5ad0f02458f8d1e95975bd83e05f946c5643200 oid sha256:bec22e56da539e1350b33f5b8dd229b57358750c6604b84471ec954a2165ec12
size 182075 size 182216
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:e78110e0a62ad1e0f83afb2a7f0d4d0212393dddb3c69fbf986387b0d02335d9 oid sha256:de97c82bf7ea565a982bc15425a77870152b7d048014751bf0850e9a416fec33
size 104721 size 104828
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:77754ddf31e79f7366b80ddd8a80b64ba17d1ca158930cdc74a0f97efc567264 oid sha256:81df879269cc8b8daa6dd7f174f290ed23e8f3d64a3e26fb4612b61db77a5907
size 147104 size 147256
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:b05a474e324855ae6a103ba650d11789b18ad5aabbf2690d5597302e58f1949b oid sha256:e971940a1cc8bec0e7485e3d320edc679bbb8ccb7968082809fabb4016c194d9
size 160311 size 160528
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:d697c4216ba4330a2a91ac058c2c96380b5f192496a1be2a33a652a922365977 oid sha256:3c8ec3fe11e9bb64c153efb74debe250acc64e2935e500bcf04cae882cf0105b
size 195262 size 195540
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:1709e4b6fa0940284e48c4b8725c79361b5618fcdc3e49db8b87ccc5b0d874d9 oid sha256:3f937fb0b3982bdea1697fa4059c94d729704cc1a31abd8c4e332e98cbe7d996
size 105070 size 105278
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:aeb53763be78d707668492260db9328a5eb570fcb4ac02a00aa661406c729447 oid sha256:dd46ac2966599153dbeca05d90ef59e1d9fe3cfbebea3726e3cbec97b89f50e2
size 152085 size 152428
@@ -28,15 +28,20 @@ class CreateRoomScreenViewModelTests: XCTestCase {
clientProxy.roomForIdentifierClosure = { roomID in .joined(JoinedRoomProxyMock(.init(id: roomID))) } clientProxy.roomForIdentifierClosure = { roomID in .joined(JoinedRoomProxyMock(.init(id: roomID))) }
userSession = UserSessionMock(.init(clientProxy: clientProxy)) userSession = UserSessionMock(.init(clientProxy: clientProxy))
ServiceLocator.shared.settings.knockingEnabled = true ServiceLocator.shared.settings.knockingEnabled = true
let viewModel = CreateRoomScreenViewModel(userSession: userSession, let viewModel = CreateRoomScreenViewModel(isSpace: false,
userSession: userSession,
analytics: ServiceLocator.shared.analytics, analytics: ServiceLocator.shared.analytics,
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: ServiceLocator.shared.settings) appSettings: ServiceLocator.shared.settings)
self.viewModel = viewModel self.viewModel = viewModel
} }
override func tearDown() {
AppSettings.resetAllSettings()
}
func testDefaultSecurity() { func testDefaultSecurity() {
XCTAssertTrue(context.viewState.bindings.isRoomPrivate) XCTAssertEqual(context.viewState.bindings.selectedAccessType, .private)
} }
func testCreateRoomRequirements() { func testCreateRoomRequirements() {
@@ -49,11 +54,11 @@ class CreateRoomScreenViewModelTests: XCTestCase {
// Given a form with a blank topic. // Given a form with a blank topic.
context.send(viewAction: .updateRoomName("A")) context.send(viewAction: .updateRoomName("A"))
context.roomTopic = "" context.roomTopic = ""
context.isRoomPrivate = false context.selectedAccessType = .private
XCTAssertTrue(context.viewState.canCreateRoom) XCTAssertTrue(context.viewState.canCreateRoom)
// When creating the room. // When creating the room.
clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReturnValue = .success("1") clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReturnValue = .success("1")
let deferred = deferFulfillment(viewModel.actions) { action in let deferred = deferFulfillment(viewModel.actions) { action in
guard case .createdRoom(let roomProxy) = action, roomProxy.id == "1" else { return false } guard case .createdRoom(let roomProxy) = action, roomProxy.id == "1" else { return false }
return true return true
@@ -62,26 +67,24 @@ class CreateRoomScreenViewModelTests: XCTestCase {
try await deferred.fulfill() try await deferred.fulfill()
// Then the room should be created and a topic should not be set. // Then the room should be created and a topic should not be set.
XCTAssertTrue(clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartCalled) XCTAssertTrue(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartCalled)
XCTAssertEqual(clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReceivedArguments?.name, "A") XCTAssertEqual(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.name, "A")
XCTAssertNil(clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartReceivedArguments?.topic, XCTAssertNil(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartReceivedArguments?.topic,
"The topic should be sent as nil when it is empty.") "The topic should be sent as nil when it is empty.")
} }
func testCreateKnockingRoom() async { func testCreateKnockingRoom() async {
context.send(viewAction: .updateRoomName("A")) context.send(viewAction: .updateRoomName("A"))
context.roomTopic = "B" 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 // 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 // so we need to wait a main actor cycle to ensure the view state is updated
await Task.yield() await Task.yield()
context.isKnockingOnly = true
XCTAssertTrue(context.viewState.canCreateRoom) XCTAssertTrue(context.viewState.canCreateRoom)
let expectation = expectation(description: "Wait for the room to be created") let expectation = expectation(description: "Wait for the room to be created")
clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartClosure = { _, _, isPrivate, isKnockingOnly, _, _, localAliasPart in clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartClosure = { _, _, accessType, _, _, _, localAliasPart in
XCTAssertTrue(isKnockingOnly) XCTAssertEqual(accessType, .askToJoin)
XCTAssertFalse(isPrivate)
XCTAssertEqual(localAliasPart, "a") XCTAssertEqual(localAliasPart, "a")
defer { expectation.fulfill() } defer { expectation.fulfill() }
return .success("") return .success("")
@@ -93,7 +96,7 @@ class CreateRoomScreenViewModelTests: XCTestCase {
func testCreatePublicRoomFailsForInvalidAlias() async throws { func testCreatePublicRoomFailsForInvalidAlias() async throws {
context.send(viewAction: .updateRoomName("A")) context.send(viewAction: .updateRoomName("A"))
context.roomTopic = "B" context.roomTopic = "B"
context.isRoomPrivate = false context.selectedAccessType = .public
// When setting the room as private we always reset the alias // 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 // so we need to wait a main actor cycle to ensure the view state is updated
await Task.yield() await Task.yield()
@@ -109,14 +112,14 @@ class CreateRoomScreenViewModelTests: XCTestCase {
// blocked it // blocked it
context.send(viewAction: .createRoom) context.send(viewAction: .createRoom)
await Task.yield() await Task.yield()
XCTAssertFalse(clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartCalled) XCTAssertFalse(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartCalled)
} }
func testCreatePublicRoomFailsForExistingAlias() async throws { func testCreatePublicRoomFailsForExistingAlias() async throws {
clientProxy.isAliasAvailableReturnValue = .success(false) clientProxy.isAliasAvailableReturnValue = .success(false)
context.send(viewAction: .updateRoomName("A")) context.send(viewAction: .updateRoomName("A"))
context.roomTopic = "B" context.roomTopic = "B"
context.isRoomPrivate = false context.selectedAccessType = .public
// When setting the room as private we always reset the alias // 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 // so we need to wait a main actor cycle to ensure the view state is updated
await Task.yield() await Task.yield()
@@ -140,27 +143,11 @@ class CreateRoomScreenViewModelTests: XCTestCase {
context.send(viewAction: .createRoom) context.send(viewAction: .createRoom)
await fulfillment(of: [expectation]) await fulfillment(of: [expectation])
XCTAssertEqual(clientProxy.isAliasAvailableCallsCount, 2) XCTAssertEqual(clientProxy.isAliasAvailableCallsCount, 2)
XCTAssertFalse(clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLAliasLocalPartCalled) XCTAssertFalse(clientProxy.createRoomNameTopicAccessTypeIsSpaceUserIDsAvatarURLAliasLocalPartCalled)
}
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])
} }
func testNameAndAddressSync() async { func testNameAndAddressSync() async {
context.isRoomPrivate = true context.selectedAccessType = .private
await Task.yield() await Task.yield()
context.send(viewAction: .updateRoomName("abc")) context.send(viewAction: .updateRoomName("abc"))
XCTAssertEqual(context.viewState.aliasLocalPart, "abc") XCTAssertEqual(context.viewState.aliasLocalPart, "abc")