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