started implementing the logic behind the access type and the creation parameters

This commit is contained in:
Mauro Romito
2026-01-19 18:21:01 +01:00
committed by Mauro
parent cea365ac97
commit 204fa5b0ca
5 changed files with 109 additions and 18 deletions
@@ -29,6 +29,7 @@ struct CreateRoomScreenViewState: BindableState {
var roomName: String var roomName: String
let serverName: String let serverName: String
let isKnockingFeatureEnabled: Bool let isKnockingFeatureEnabled: Bool
let canSelectSpace: Bool
var aliasLocalPart: String var aliasLocalPart: String
var bindings: CreateRoomScreenViewStateBindings var bindings: CreateRoomScreenViewStateBindings
var avatarMediaInfo: MediaInfo? { var avatarMediaInfo: MediaInfo? {
@@ -59,18 +60,46 @@ struct CreateRoomScreenViewState: BindableState {
} }
} }
var availableAccessTypes: [CreateRoomAccessType] { var selectedSpace: SpaceServiceRoomProtocol?
var availableTypes = CreateRoomAccessType.allCases
if isSpace || !isKnockingFeatureEnabled { var availableAccessTypes: [CreateRoomScreenAccessType] {
availableTypes.removeAll { $0 == .askToJoin } var availableAccessTypes: [CreateRoomScreenAccessType] = []
if isSpace {
availableAccessTypes = [.public]
} else if let selectedSpace, selectedSpace.joinRule != .public {
availableAccessTypes = [.spaceMembers]
if isKnockingFeatureEnabled {
availableAccessTypes.append(.askToJoinWithSpaceMembers)
}
} else {
availableAccessTypes = [.public]
if isKnockingFeatureEnabled {
availableAccessTypes.append(.askToJoin)
}
}
availableAccessTypes.append(.private)
return availableAccessTypes
}
var roomAccessType: CreateRoomAccessType {
switch bindings.selectedAccessType {
case .public:
return .public
case .spaceMembers:
return .spaceMembers(spaceID: selectedSpace?.id ?? "")
case .askToJoinWithSpaceMembers:
return .askToJoinWithSpaceMembers(spaceID: selectedSpace?.id ?? "")
case .askToJoin:
return .askToJoin
case .private:
return .private
} }
return availableTypes
} }
} }
struct CreateRoomScreenViewStateBindings { struct CreateRoomScreenViewStateBindings {
var roomTopic: String var roomTopic: String
var selectedAccessType: CreateRoomAccessType var selectedAccessType: CreateRoomScreenAccessType
var showAttachmentConfirmationDialog = false var showAttachmentConfirmationDialog = false
/// Information describing the currently displayed alert. /// Information describing the currently displayed alert.
@@ -102,3 +131,11 @@ extension Set<CreateRoomScreenAliasErrorState> {
return nil return nil
} }
} }
enum CreateRoomScreenAccessType {
case `public`
case spaceMembers
case askToJoinWithSpaceMembers
case askToJoin
case `private`
}
@@ -45,6 +45,7 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
roomName: "", roomName: "",
serverName: userSession.clientProxy.userIDServerName ?? "", serverName: userSession.clientProxy.userIDServerName ?? "",
isKnockingFeatureEnabled: appSettings.knockingEnabled, isKnockingFeatureEnabled: appSettings.knockingEnabled,
canSelectSpace: isSpace ? false : appSettings.createSpaceEnabled,
aliasLocalPart: roomAliasNameFromRoomDisplayName(roomName: ""), aliasLocalPart: roomAliasNameFromRoomDisplayName(roomName: ""),
bindings: bindings), bindings: bindings),
mediaProvider: userSession.mediaProvider) mediaProvider: userSession.mediaProvider)
@@ -114,9 +115,9 @@ 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.selectedAccessType) .map(\.roomAccessType)
.removeDuplicates()
.filter(\.isPrivate) .filter(\.isPrivate)
.removeDuplicates()
.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 }
@@ -135,7 +136,7 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
return return
} }
guard !state.bindings.selectedAccessType.isPrivate, guard !state.roomAccessType.isPrivate,
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
@@ -167,6 +168,18 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
} }
} }
.store(in: &cancellables) .store(in: &cancellables)
context.$viewState
.map(\.availableAccessTypes)
.removeDuplicates()
.receive(on: DispatchQueue.main)
.sink { [weak self] availableAccessTypes in
guard let self else { return }
if !availableAccessTypes.contains(state.bindings.selectedAccessType) {
state.bindings.selectedAccessType = .private
}
}
.store(in: &cancellables)
} }
private func createRoom() async { private func createRoom() async {
@@ -176,7 +189,7 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
showLoadingIndicator() showLoadingIndicator()
// 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.bindings.selectedAccessType.isPrivate { if !state.roomAccessType.isPrivate {
guard let canonicalAlias = String.makeCanonicalAlias(aliasLocalPart: state.aliasLocalPart, guard let canonicalAlias = String.makeCanonicalAlias(aliasLocalPart: state.aliasLocalPart,
serverName: state.serverName), serverName: state.serverName),
isRoomAliasFormatValid(alias: canonicalAlias) else { isRoomAliasFormatValid(alias: canonicalAlias) else {
@@ -224,11 +237,11 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
switch await userSession.clientProxy.createRoom(name: state.roomName, switch await userSession.clientProxy.createRoom(name: state.roomName,
topic: state.bindings.roomTopic.isBlank ? nil : state.bindings.roomTopic, topic: state.bindings.roomTopic.isBlank ? nil : state.bindings.roomTopic,
accessType: state.bindings.selectedAccessType, accessType: state.roomAccessType,
isSpace: state.isSpace, isSpace: state.isSpace,
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: state.bindings.selectedAccessType.isPrivate ? nil : state.aliasLocalPart) { aliasLocalPart: state.roomAccessType.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,8 +39,11 @@ struct CreateRoomScreen: View {
Form { Form {
roomSection roomSection
topicSection topicSection
if context.viewState.canSelectSpace {
selectSpaceSection
}
roomAccessSection roomAccessSection
if !context.selectedAccessType.isPrivate { if !context.viewState.roomAccessType.isPrivate {
roomAliasSection roomAliasSection
} }
} }
@@ -181,6 +184,7 @@ struct CreateRoomScreen: View {
Section { Section {
ForEach(context.viewState.availableAccessTypes, id: \.self) { accessType in ForEach(context.viewState.availableAccessTypes, id: \.self) { accessType in
CreateRoomAccessRow(access: accessType, CreateRoomAccessRow(access: accessType,
spaceName: context.viewState.selectedSpace?.name ?? "",
isSelected: context.selectedAccessType == accessType) { isSelected: context.selectedAccessType == accessType) {
context.selectedAccessType = accessType context.selectedAccessType = accessType
} }
@@ -215,6 +219,15 @@ struct CreateRoomScreen: View {
} }
} }
private var selectSpaceSection: some View {
Section {
EmptyView()
} header: {
Text(L10n.commonSpace)
.compoundListSectionHeader()
}
}
@ToolbarContentBuilder @ToolbarContentBuilder
private var toolbar: some ToolbarContent { private var toolbar: some ToolbarContent {
if context.viewState.shouldShowCancelButton { if context.viewState.shouldShowCancelButton {
@@ -237,7 +250,8 @@ struct CreateRoomScreen: View {
} }
private struct CreateRoomAccessRow: View { private struct CreateRoomAccessRow: View {
let access: CreateRoomAccessType let access: CreateRoomScreenAccessType
let spaceName: String
let isSelected: Bool let isSelected: Bool
let onSelection: () -> Void let onSelection: () -> Void
@@ -249,6 +263,10 @@ private struct CreateRoomAccessRow: View {
L10n.screenCreateRoomRoomAccessSectionKnockingOptionTitle L10n.screenCreateRoomRoomAccessSectionKnockingOptionTitle
case .private: case .private:
L10n.screenCreateRoomRoomAccessSectionPrivateOptionTitle L10n.screenCreateRoomRoomAccessSectionPrivateOptionTitle
case .spaceMembers:
L10n.screenCreateRoomRoomAccessSectionRestrictedOptionTitle
case .askToJoinWithSpaceMembers:
L10n.screenCreateRoomRoomAccessSectionKnockingRestrictedOptionTitle
} }
} }
@@ -260,6 +278,10 @@ private struct CreateRoomAccessRow: View {
L10n.screenCreateRoomRoomAccessSectionKnockingOptionDescription L10n.screenCreateRoomRoomAccessSectionKnockingOptionDescription
case .private: case .private:
L10n.screenCreateRoomRoomAccessSectionPrivateOptionDescription L10n.screenCreateRoomRoomAccessSectionPrivateOptionDescription
case .spaceMembers:
L10n.screenCreateRoomRoomAccessSectionRestrictedOptionDescription(spaceName)
case .askToJoinWithSpaceMembers:
L10n.screenCreateRoomRoomAccessSectionKnockingRestrictedOptionDescription(spaceName)
} }
} }
@@ -271,6 +293,10 @@ private struct CreateRoomAccessRow: View {
\.userAdd \.userAdd
case .private: case .private:
\.lock \.lock
case .spaceMembers:
\.space
case .askToJoinWithSpaceMembers:
\.userAdd
} }
} }
@@ -513,7 +513,7 @@ class ClientProxy: ClientProxyProtocol {
Self.standardSpaceCreationPowerLevelOverrides Self.standardSpaceCreationPowerLevelOverrides
} }
} else { } else {
if accessType == .askToJoin { if accessType.isAskToJoin {
Self.knockingRoomCreationPowerLevelOverrides Self.knockingRoomCreationPowerLevelOverrides
} else { } else {
Self.roomCreationPowerLevelOverrides Self.roomCreationPowerLevelOverrides
@@ -1372,7 +1372,7 @@ private extension CreateRoomAccessType {
switch self { switch self {
case .public: case .public:
false false
case .askToJoin, .private: default:
true true
} }
} }
@@ -1393,8 +1393,21 @@ private extension CreateRoomAccessType {
switch self { switch self {
case .askToJoin: case .askToJoin:
.knock .knock
case .spaceMembers(let spaceID):
.restricted(rules: [.roomMembership(roomId: spaceID)])
case .askToJoinWithSpaceMembers(let spaceID):
.knockRestricted(rules: [.roomMembership(roomId: spaceID)])
case .private, .public: case .private, .public:
nil nil
} }
} }
var isAskToJoin: Bool {
switch self {
case .askToJoin, .askToJoinWithSpaceMembers:
true
default:
false
}
}
} }
@@ -48,14 +48,16 @@ enum SlidingSyncConstants {
static let maximumVisibleRangeSize = 30 static let maximumVisibleRangeSize = 30
} }
enum CreateRoomAccessType: CaseIterable { enum CreateRoomAccessType: Equatable {
case `public` case `public`
case spaceMembers(spaceID: String)
case askToJoinWithSpaceMembers(spaceID: String)
case askToJoin case askToJoin
case `private` case `private`
var isPrivate: Bool { var isPrivate: Bool {
switch self { switch self {
case .private: case .private, .spaceMembers, .askToJoinWithSpaceMembers:
true true
case .public, .askToJoin: case .public, .askToJoin:
false false