single space access implementation
also added the copies for the single and multiple spaces selection
This commit is contained in:
@@ -648,11 +648,15 @@
|
||||
"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_footer" = "Choose which spaces’ members can join this room without an invitation. %1$@";
|
||||
"screen_security_and_privacy_room_access_footer_manage_spaces_action" = "Manage spaces";
|
||||
"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_section_header" = "Access";
|
||||
"screen_security_and_privacy_room_access_space_members_option_description" = "Spaces are not currently supported";
|
||||
"screen_security_and_privacy_room_access_space_members_option_multiple_parents_description" = "Anyone in authorized spaces can join.";
|
||||
"screen_security_and_privacy_room_access_space_members_option_single_parent_description" = "Anyone in %1$@ can join.";
|
||||
"screen_security_and_privacy_room_access_space_members_option_title" = "Space members";
|
||||
"screen_security_and_privacy_room_access_space_members_option_unavailable_description" = "Spaces are not currently supported";
|
||||
"screen_security_and_privacy_room_address_section_header" = "Address";
|
||||
"screen_security_and_privacy_room_directory_visibility_section_footer" = "Allow for this room to be found by searching %1$@ public room directory";
|
||||
"screen_security_and_privacy_room_directory_visibility_toggle_description" = "Allow to be found by searching the public directory.";
|
||||
|
||||
@@ -648,11 +648,15 @@
|
||||
"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_footer" = "Choose which spaces’ members can join this room without an invitation. %1$@";
|
||||
"screen_security_and_privacy_room_access_footer_manage_spaces_action" = "Manage spaces";
|
||||
"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_section_header" = "Access";
|
||||
"screen_security_and_privacy_room_access_space_members_option_description" = "Spaces are not currently supported";
|
||||
"screen_security_and_privacy_room_access_space_members_option_multiple_parents_description" = "Anyone in authorized spaces can join.";
|
||||
"screen_security_and_privacy_room_access_space_members_option_single_parent_description" = "Anyone in %1$@ can join.";
|
||||
"screen_security_and_privacy_room_access_space_members_option_title" = "Space members";
|
||||
"screen_security_and_privacy_room_access_space_members_option_unavailable_description" = "Spaces are not currently supported";
|
||||
"screen_security_and_privacy_room_address_section_header" = "Address";
|
||||
"screen_security_and_privacy_room_directory_visibility_section_footer" = "Allow for this room to be found by searching %1$@ public room directory";
|
||||
"screen_security_and_privacy_room_directory_visibility_toggle_description" = "Allow to be found by searching the public directory.";
|
||||
|
||||
@@ -2768,16 +2768,28 @@ internal enum L10n {
|
||||
internal static var screenSecurityAndPrivacyRoomAccessAnyoneOptionDescription: String { return L10n.tr("Localizable", "screen_security_and_privacy_room_access_anyone_option_description") }
|
||||
/// Anyone
|
||||
internal static var screenSecurityAndPrivacyRoomAccessAnyoneOptionTitle: String { return L10n.tr("Localizable", "screen_security_and_privacy_room_access_anyone_option_title") }
|
||||
/// Choose which spaces’ members can join this room without an invitation. %1$@
|
||||
internal static func screenSecurityAndPrivacyRoomAccessFooter(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "screen_security_and_privacy_room_access_footer", String(describing: p1))
|
||||
}
|
||||
/// Manage spaces
|
||||
internal static var screenSecurityAndPrivacyRoomAccessFooterManageSpacesAction: String { return L10n.tr("Localizable", "screen_security_and_privacy_room_access_footer_manage_spaces_action") }
|
||||
/// Only invited people can join.
|
||||
internal static var screenSecurityAndPrivacyRoomAccessInviteOnlyOptionDescription: String { return L10n.tr("Localizable", "screen_security_and_privacy_room_access_invite_only_option_description") }
|
||||
/// Invite only
|
||||
internal static var screenSecurityAndPrivacyRoomAccessInviteOnlyOptionTitle: String { return L10n.tr("Localizable", "screen_security_and_privacy_room_access_invite_only_option_title") }
|
||||
/// Access
|
||||
internal static var screenSecurityAndPrivacyRoomAccessSectionHeader: String { return L10n.tr("Localizable", "screen_security_and_privacy_room_access_section_header") }
|
||||
/// Spaces are not currently supported
|
||||
internal static var screenSecurityAndPrivacyRoomAccessSpaceMembersOptionDescription: String { return L10n.tr("Localizable", "screen_security_and_privacy_room_access_space_members_option_description") }
|
||||
/// Anyone in authorized spaces can join.
|
||||
internal static var screenSecurityAndPrivacyRoomAccessSpaceMembersOptionMultipleParentsDescription: String { return L10n.tr("Localizable", "screen_security_and_privacy_room_access_space_members_option_multiple_parents_description") }
|
||||
/// Anyone in %1$@ can join.
|
||||
internal static func screenSecurityAndPrivacyRoomAccessSpaceMembersOptionSingleParentDescription(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "screen_security_and_privacy_room_access_space_members_option_single_parent_description", String(describing: p1))
|
||||
}
|
||||
/// Space members
|
||||
internal static var screenSecurityAndPrivacyRoomAccessSpaceMembersOptionTitle: String { return L10n.tr("Localizable", "screen_security_and_privacy_room_access_space_members_option_title") }
|
||||
/// Spaces are not currently supported
|
||||
internal static var screenSecurityAndPrivacyRoomAccessSpaceMembersOptionUnavailableDescription: String { return L10n.tr("Localizable", "screen_security_and_privacy_room_access_space_members_option_unavailable_description") }
|
||||
/// You’ll need an address in order to make it visible in the public directory.
|
||||
internal static var screenSecurityAndPrivacyRoomAddressSectionFooter: String { return L10n.tr("Localizable", "screen_security_and_privacy_room_address_section_footer") }
|
||||
/// Address
|
||||
|
||||
@@ -16616,6 +16616,76 @@ class SpaceServiceProxyMock: SpaceServiceProxyProtocol, @unchecked Sendable {
|
||||
return leaveSpaceSpaceIDReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - joinedParents
|
||||
|
||||
var joinedParentsRoomIDUnderlyingCallsCount = 0
|
||||
var joinedParentsRoomIDCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return joinedParentsRoomIDUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = joinedParentsRoomIDUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
joinedParentsRoomIDUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
joinedParentsRoomIDUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var joinedParentsRoomIDCalled: Bool {
|
||||
return joinedParentsRoomIDCallsCount > 0
|
||||
}
|
||||
var joinedParentsRoomIDReceivedRoomID: String?
|
||||
var joinedParentsRoomIDReceivedInvocations: [String] = []
|
||||
|
||||
var joinedParentsRoomIDUnderlyingReturnValue: Result<[SpaceRoomProxyProtocol], SpaceServiceProxyError>!
|
||||
var joinedParentsRoomIDReturnValue: Result<[SpaceRoomProxyProtocol], SpaceServiceProxyError>! {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return joinedParentsRoomIDUnderlyingReturnValue
|
||||
} else {
|
||||
var returnValue: Result<[SpaceRoomProxyProtocol], SpaceServiceProxyError>? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = joinedParentsRoomIDUnderlyingReturnValue
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
joinedParentsRoomIDUnderlyingReturnValue = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
joinedParentsRoomIDUnderlyingReturnValue = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var joinedParentsRoomIDClosure: ((String) async -> Result<[SpaceRoomProxyProtocol], SpaceServiceProxyError>)?
|
||||
|
||||
func joinedParents(roomID: String) async -> Result<[SpaceRoomProxyProtocol], SpaceServiceProxyError> {
|
||||
joinedParentsRoomIDCallsCount += 1
|
||||
joinedParentsRoomIDReceivedRoomID = roomID
|
||||
DispatchQueue.main.async {
|
||||
self.joinedParentsRoomIDReceivedInvocations.append(roomID)
|
||||
}
|
||||
if let joinedParentsRoomIDClosure = joinedParentsRoomIDClosure {
|
||||
return await joinedParentsRoomIDClosure(roomID)
|
||||
} else {
|
||||
return joinedParentsRoomIDReturnValue
|
||||
}
|
||||
}
|
||||
}
|
||||
class StaticRoomSummaryProviderMock: StaticRoomSummaryProviderProtocol, @unchecked Sendable {
|
||||
var statePublisher: CurrentValuePublisher<RoomSummaryProviderState, Never> {
|
||||
|
||||
@@ -13,6 +13,7 @@ import MatrixRustSDK
|
||||
extension SpaceServiceProxyMock {
|
||||
struct Configuration {
|
||||
var joinedSpaces: [SpaceRoomProxyProtocol] = []
|
||||
var joinedParentSpaces: [SpaceRoomProxyProtocol] = []
|
||||
var spaceRoomLists: [String: SpaceRoomListProxyMock] = [:]
|
||||
var leaveSpaceRooms: [LeaveSpaceRoom] = []
|
||||
}
|
||||
@@ -21,6 +22,7 @@ extension SpaceServiceProxyMock {
|
||||
self.init()
|
||||
|
||||
joinedSpacesPublisher = .init(configuration.joinedSpaces)
|
||||
joinedParentsRoomIDReturnValue = .success(configuration.joinedParentSpaces)
|
||||
spaceRoomListSpaceIDClosure = { spaceID in
|
||||
if let spaceRoomList = configuration.spaceRoomLists[spaceID] {
|
||||
.success(spaceRoomList)
|
||||
|
||||
@@ -7,23 +7,45 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
enum SecurityAndPrivacyScreenViewModelAction {
|
||||
case displayEditAddressScreen
|
||||
}
|
||||
|
||||
struct SecurityAndPrivacyScreenViewState: BindableState {
|
||||
private static let accessSectionFooterAttributedString = {
|
||||
let linkPlaceholder = "{link}"
|
||||
var footer = AttributedString(L10n.screenSecurityAndPrivacyRoomAccessFooter(linkPlaceholder))
|
||||
var linkString = AttributedString(L10n.screenSecurityAndPrivacyRoomAccessFooterManageSpacesAction)
|
||||
// Doesn't really matter
|
||||
linkString.link = .init(stringLiteral: "action://manageSpace")
|
||||
linkString.bold()
|
||||
footer.replace(linkPlaceholder, with: linkString)
|
||||
return footer
|
||||
}()
|
||||
|
||||
let serverName: String
|
||||
var currentSettings: SecurityAndPrivacySettings
|
||||
var bindings: SecurityAndPrivacyScreenViewStateBindings
|
||||
var canonicalAlias: String?
|
||||
var isKnockingEnabled: Bool
|
||||
var isSpaceSettingsEnabled: Bool
|
||||
var isSpace: Bool
|
||||
|
||||
var canEditAddress = false
|
||||
var canEditJoinRule = false
|
||||
var canEnableEncryption = false
|
||||
var canEditHistoryVisibility = false
|
||||
var joinedParentSpaces: [SpaceRoomProxyProtocol] = []
|
||||
|
||||
var selectableSpacesCount: Int {
|
||||
Set(joinedParentSpaces.map(\.id) + currentSettings.accessType.spaceIDs).count
|
||||
}
|
||||
|
||||
private var desiredJoinedParentSpaces: [SpaceRoomProxyProtocol] {
|
||||
joinedParentSpaces.filter { bindings.desiredSettings.accessType.spaceIDs.contains($0.id) }
|
||||
}
|
||||
|
||||
private var hasChanges: Bool {
|
||||
currentSettings != bindings.desiredSettings
|
||||
@@ -45,16 +67,77 @@ struct SecurityAndPrivacyScreenViewState: BindableState {
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
var isSpaceMembersOptionAvailable: Bool {
|
||||
currentSettings.accessType.isSpaceUsers || isSpaceMembersOptionSelectable
|
||||
}
|
||||
|
||||
var isSpaceMembersOptionSelectable: Bool {
|
||||
isSpaceSettingsEnabled && selectableSpacesCount > 0
|
||||
}
|
||||
|
||||
var spaceMembersDescription: String {
|
||||
if isSpaceMembersOptionSelectable {
|
||||
switch spaceSelection {
|
||||
case .singleJoined(let joinedParentSpace):
|
||||
L10n.screenSecurityAndPrivacyRoomAccessSpaceMembersOptionSingleParentDescription(joinedParentSpace.name)
|
||||
case .singleUnknown(let id):
|
||||
L10n.screenSecurityAndPrivacyRoomAccessSpaceMembersOptionSingleParentDescription(id)
|
||||
case .multiple:
|
||||
L10n.screenSecurityAndPrivacyRoomAccessSpaceMembersOptionMultipleParentsDescription
|
||||
}
|
||||
} else {
|
||||
L10n.screenSecurityAndPrivacyRoomAccessSpaceMembersOptionUnavailableDescription
|
||||
}
|
||||
}
|
||||
|
||||
var accessSectionFooter: AttributedString? {
|
||||
if bindings.desiredSettings.accessType.isSpaceUsers, isSpaceMembersOptionSelectable, selectableSpacesCount > 1 {
|
||||
Self.accessSectionFooterAttributedString
|
||||
} else {
|
||||
nil
|
||||
}
|
||||
}
|
||||
|
||||
enum SpaceSelection {
|
||||
/// There is only one available parent space for selection and is joined by the user
|
||||
case singleJoined(SpaceRoomProxyProtocol)
|
||||
/// There is only one available space for selection and is unknown to the user
|
||||
case singleUnknown(id: String)
|
||||
/// Multiple spaces are available for selection
|
||||
case multiple
|
||||
}
|
||||
|
||||
var spaceSelection: SpaceSelection {
|
||||
if selectableSpacesCount > 1 {
|
||||
.multiple
|
||||
} else if let desiredJoinedParent = desiredJoinedParentSpaces.first {
|
||||
// The parent space is joined by the user and is also currently selected
|
||||
.singleJoined(desiredJoinedParent)
|
||||
} else if let joinedParent = joinedParentSpaces.first {
|
||||
// The parent space is joined by the user but is not currently selected
|
||||
.singleJoined(joinedParent)
|
||||
} else if let unknownSpaceID = bindings.desiredSettings.accessType.spaceIDs.first {
|
||||
// The space is not joined by the user but is currently selected
|
||||
.singleUnknown(id: unknownSpaceID)
|
||||
} else {
|
||||
// Not reachable because it would mean the selectable spaces are more than 1
|
||||
// but are neither selected and/or joined parents.
|
||||
fatalError("Not reachable")
|
||||
}
|
||||
}
|
||||
|
||||
init(serverName: String,
|
||||
accessType: SecurityAndPrivacyRoomAccessType,
|
||||
isEncryptionEnabled: Bool,
|
||||
historyVisibility: SecurityAndPrivacyHistoryVisibility,
|
||||
isSpace: Bool,
|
||||
isKnockingEnabled: Bool) {
|
||||
isKnockingEnabled: Bool,
|
||||
isSpaceSettingsEnabled: Bool) {
|
||||
self.serverName = serverName
|
||||
self.isKnockingEnabled = isKnockingEnabled
|
||||
self.isSpace = isSpace
|
||||
self.isSpaceSettingsEnabled = isSpaceSettingsEnabled
|
||||
|
||||
let settings = SecurityAndPrivacySettings(accessType: accessType,
|
||||
isEncryptionEnabled: isEncryptionEnabled,
|
||||
@@ -76,11 +159,39 @@ struct SecurityAndPrivacySettings: Equatable {
|
||||
var isVisibileInRoomDirectory: Bool?
|
||||
}
|
||||
|
||||
enum SecurityAndPrivacyRoomAccessType {
|
||||
enum SecurityAndPrivacyRoomAccessType: Equatable {
|
||||
case inviteOnly
|
||||
case askToJoin
|
||||
case askToJoinWithSpaceUsers(spaceIDs: [String])
|
||||
case anyone
|
||||
case spaceUsers
|
||||
case spaceUsers(spaceIDs: [String])
|
||||
|
||||
var isSpaceUsers: Bool {
|
||||
switch self {
|
||||
case .spaceUsers:
|
||||
true
|
||||
default:
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
var isAddressRequired: Bool {
|
||||
switch self {
|
||||
case .inviteOnly, .spaceUsers:
|
||||
false
|
||||
case .anyone, .askToJoin, .askToJoinWithSpaceUsers:
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
var spaceIDs: [String] {
|
||||
switch self {
|
||||
case .spaceUsers(let spaceIDs), .askToJoinWithSpaceUsers(let spaceIDs):
|
||||
return spaceIDs
|
||||
case .inviteOnly, .askToJoin, .anyone:
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum SecurityAndPrivacyAlertType {
|
||||
@@ -91,6 +202,8 @@ enum SecurityAndPrivacyScreenViewAction {
|
||||
case save
|
||||
case tryUpdatingEncryption(Bool)
|
||||
case editAddress
|
||||
case selectedSpaceMembersAccess
|
||||
case manageSpaces
|
||||
}
|
||||
|
||||
enum SecurityAndPrivacyHistoryVisibility {
|
||||
|
||||
@@ -37,7 +37,8 @@ class SecurityAndPrivacyScreenViewModel: SecurityAndPrivacyScreenViewModelType,
|
||||
isEncryptionEnabled: roomProxy.infoPublisher.value.isEncrypted,
|
||||
historyVisibility: roomProxy.infoPublisher.value.historyVisibility.toSecurityAndPrivacyHistoryVisibility,
|
||||
isSpace: roomProxy.infoPublisher.value.isSpace,
|
||||
isKnockingEnabled: appSettings.knockingEnabled))
|
||||
isKnockingEnabled: appSettings.knockingEnabled,
|
||||
isSpaceSettingsEnabled: appSettings.spaceSettingsEnabled))
|
||||
|
||||
if let powerLevels = roomProxy.infoPublisher.value.powerLevels {
|
||||
setupPermissions(powerLevels: powerLevels)
|
||||
@@ -45,6 +46,14 @@ class SecurityAndPrivacyScreenViewModel: SecurityAndPrivacyScreenViewModelType,
|
||||
|
||||
setupRoomDirectoryVisibility()
|
||||
setupSubscriptions()
|
||||
Task {
|
||||
switch await clientProxy.spaceService.joinedParents(roomID: roomProxy.id) {
|
||||
case .success(let joinedParentSpaces):
|
||||
state.joinedParentSpaces = joinedParentSpaces
|
||||
case .failure:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
@@ -69,6 +78,11 @@ class SecurityAndPrivacyScreenViewModel: SecurityAndPrivacyScreenViewModelType,
|
||||
}
|
||||
case .editAddress:
|
||||
actionsSubject.send(.displayEditAddressScreen)
|
||||
case .selectedSpaceMembersAccess:
|
||||
handleSelectedSpaceMembersAccess()
|
||||
case .manageSpaces:
|
||||
// TODO: Implement multiple space selection
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +141,10 @@ class SecurityAndPrivacyScreenViewModel: SecurityAndPrivacyScreenViewModelType,
|
||||
appSettings.$knockingEnabled
|
||||
.weakAssign(to: \.state.isKnockingEnabled, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
appSettings.$spaceSettingsEnabled
|
||||
.weakAssign(to: \.state.isSpaceSettingsEnabled, on: self)
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
private func setupPermissions(powerLevels: RoomPowerLevelsProxyProtocol) {
|
||||
@@ -202,6 +220,18 @@ class SecurityAndPrivacyScreenViewModel: SecurityAndPrivacyScreenViewModelType,
|
||||
}
|
||||
}
|
||||
|
||||
private func handleSelectedSpaceMembersAccess() {
|
||||
switch context.viewState.spaceSelection {
|
||||
case .singleJoined(let joinedParent):
|
||||
context.desiredSettings.accessType = .spaceUsers(spaceIDs: [joinedParent.id])
|
||||
case .singleUnknown(let id):
|
||||
context.desiredSettings.accessType = .spaceUsers(spaceIDs: [id])
|
||||
case .multiple:
|
||||
// TODO: Implement multiple space selection
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private static let loadingIndicatorIdentifier = "\(EditRoomAddressScreenViewModel.self)-Loading"
|
||||
|
||||
private func showLoadingIndicator() {
|
||||
@@ -225,25 +255,10 @@ private extension SecurityAndPrivacyRoomAccessType {
|
||||
.knock
|
||||
case .anyone:
|
||||
.public
|
||||
case .spaceUsers:
|
||||
fatalError("The user shouldn't be able to select this rule")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension Optional where Wrapped == JoinRule {
|
||||
var toSecurityAndPrivacyRoomAccessType: SecurityAndPrivacyRoomAccessType {
|
||||
switch self {
|
||||
case .none, .public:
|
||||
return .anyone
|
||||
case .invite:
|
||||
return .inviteOnly
|
||||
case .knock, .knockRestricted:
|
||||
return .askToJoin
|
||||
case .restricted:
|
||||
return .spaceUsers
|
||||
default:
|
||||
return .inviteOnly
|
||||
case .spaceUsers(let spaceIDs):
|
||||
.restricted(rules: spaceIDs.map { .roomMembership(roomId: $0) })
|
||||
case .askToJoinWithSpaceUsers(let spaceIDs):
|
||||
.knockRestricted(rules: spaceIDs.map { .roomMembership(roomId: $0) })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,3 +288,28 @@ private extension SecurityAndPrivacyHistoryVisibility {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension Optional where Wrapped == JoinRule {
|
||||
var toSecurityAndPrivacyRoomAccessType: SecurityAndPrivacyRoomAccessType {
|
||||
switch self {
|
||||
case .none, .public:
|
||||
return .anyone
|
||||
case .invite:
|
||||
return .inviteOnly
|
||||
case .knock, .knockRestricted:
|
||||
// TODO: Handle knock restricted with rules
|
||||
return .askToJoin
|
||||
case .restricted(let rules):
|
||||
let spaceIDs = rules.compactMap { rule in
|
||||
if case let .roomMembership(id) = rule {
|
||||
id
|
||||
} else {
|
||||
nil
|
||||
}
|
||||
}
|
||||
return .spaceUsers(spaceIDs: spaceIDs)
|
||||
default:
|
||||
return .inviteOnly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ struct SecurityAndPrivacyScreen: View {
|
||||
roomAccessSection
|
||||
}
|
||||
|
||||
if context.desiredSettings.accessType != .inviteOnly, context.viewState.canEditAddress {
|
||||
if context.desiredSettings.accessType.isAddressRequired, context.viewState.canEditAddress {
|
||||
visibilitySection
|
||||
if let canonicalAlias = context.viewState.canonicalAlias {
|
||||
addressSection(canonicalAlias: canonicalAlias)
|
||||
@@ -57,13 +57,14 @@ struct SecurityAndPrivacyScreen: View {
|
||||
.disabled(!context.viewState.isKnockingEnabled)
|
||||
}
|
||||
|
||||
if context.viewState.currentSettings.accessType == .spaceUsers {
|
||||
if context.viewState.isSpaceMembersOptionAvailable {
|
||||
ListRow(label: .default(title: L10n.screenSecurityAndPrivacyRoomAccessSpaceMembersOptionTitle,
|
||||
description: L10n.screenSecurityAndPrivacyRoomAccessSpaceMembersOptionDescription,
|
||||
description: context.viewState.spaceMembersDescription,
|
||||
icon: \.space),
|
||||
kind: .selection(isSelected: context.desiredSettings.accessType == .spaceUsers) { })
|
||||
// This is not supported so it will always be disabled and has no handler
|
||||
.disabled(true)
|
||||
kind: .selection(isSelected: context.desiredSettings.accessType.isSpaceUsers) {
|
||||
context.send(viewAction: .selectedSpaceMembersAccess)
|
||||
})
|
||||
.disabled(!context.viewState.isSpaceMembersOptionSelectable)
|
||||
}
|
||||
|
||||
ListRow(label: .default(title: L10n.screenSecurityAndPrivacyRoomAccessInviteOnlyOptionTitle,
|
||||
@@ -73,6 +74,15 @@ struct SecurityAndPrivacyScreen: View {
|
||||
} header: {
|
||||
Text(L10n.screenSecurityAndPrivacyRoomAccessSectionHeader)
|
||||
.compoundListSectionHeader()
|
||||
} footer: {
|
||||
if let footer = context.viewState.accessSectionFooter {
|
||||
Text(footer)
|
||||
.compoundListSectionFooter()
|
||||
.environment(\.openURL, OpenURLAction { _ in
|
||||
context.send(viewAction: .manageSpaces)
|
||||
return .handled
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,14 +228,36 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
|
||||
appSettings: AppSettings())
|
||||
}()
|
||||
|
||||
static let restrictedViewModel = {
|
||||
static let singleSpaceMembersViewModel = {
|
||||
AppSettings.resetAllSettings()
|
||||
let appSettings = AppSettings()
|
||||
appSettings.spaceSettingsEnabled = true
|
||||
let space = [SpaceRoomProxyProtocol].mockSingleRoom[0]
|
||||
|
||||
return SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false,
|
||||
canonicalAlias: "#room:matrix.org",
|
||||
members: .allMembersAsCreator,
|
||||
joinRule: .restricted(rules: []),
|
||||
joinRule: .restricted(rules: [.roomMembership(roomId: space.id)]),
|
||||
isVisibleInPublicDirectory: true)),
|
||||
clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org")),
|
||||
clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org",
|
||||
spaceServiceConfiguration: .init(joinedParentSpaces: [space]))),
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: AppSettings())
|
||||
}()
|
||||
|
||||
static let multipleSpacesMembersViewModel = {
|
||||
AppSettings.resetAllSettings()
|
||||
let appSettings = AppSettings()
|
||||
appSettings.spaceSettingsEnabled = true
|
||||
let spaces = [SpaceRoomProxyProtocol].mockJoinedSpaces
|
||||
|
||||
return SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false,
|
||||
canonicalAlias: "#room:matrix.org",
|
||||
members: .allMembersAsCreator,
|
||||
joinRule: .restricted(rules: spaces.map { .roomMembership(roomId: $0.id) }),
|
||||
isVisibleInPublicDirectory: true)),
|
||||
clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org",
|
||||
spaceServiceConfiguration: .init(joinedParentSpaces: spaces))),
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: AppSettings())
|
||||
}()
|
||||
@@ -278,17 +310,25 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
|
||||
.previewDisplayName("Public room without address")
|
||||
|
||||
NavigationStack {
|
||||
SecurityAndPrivacyScreen(context: restrictedViewModel.context)
|
||||
SecurityAndPrivacyScreen(context: singleSpaceMembersViewModel.context)
|
||||
}
|
||||
.snapshotPreferences(expect: restrictedViewModel.context.$viewState.map { state in
|
||||
.snapshotPreferences(expect: singleSpaceMembersViewModel.context.$viewState.map { state in
|
||||
state.currentSettings.isVisibileInRoomDirectory == true
|
||||
})
|
||||
.previewDisplayName("Restricted room")
|
||||
.previewDisplayName("Space members")
|
||||
|
||||
NavigationStack {
|
||||
SecurityAndPrivacyScreen(context: multipleSpacesMembersViewModel.context)
|
||||
}
|
||||
.snapshotPreferences(expect: multipleSpacesMembersViewModel.context.$viewState.map { state in
|
||||
state.currentSettings.isVisibileInRoomDirectory == true
|
||||
})
|
||||
.previewDisplayName("Multiple Spaces members")
|
||||
|
||||
NavigationStack {
|
||||
SecurityAndPrivacyScreen(context: askToJoinViewModel.context)
|
||||
}
|
||||
.snapshotPreferences(expect: restrictedViewModel.context.$viewState.map { state in
|
||||
.snapshotPreferences(expect: askToJoinViewModel.context.$viewState.map { state in
|
||||
state.currentSettings.isVisibileInRoomDirectory == true
|
||||
})
|
||||
.previewDisplayName("Ask to join room")
|
||||
@@ -296,7 +336,7 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
|
||||
NavigationStack {
|
||||
SecurityAndPrivacyScreen(context: publicSpaceViewModel.context)
|
||||
}
|
||||
.snapshotPreferences(expect: restrictedViewModel.context.$viewState.map { state in
|
||||
.snapshotPreferences(expect: publicSpaceViewModel.context.$viewState.map { state in
|
||||
state.currentSettings.isVisibileInRoomDirectory == true
|
||||
})
|
||||
.previewDisplayName("Public space")
|
||||
|
||||
@@ -49,6 +49,15 @@ class SpaceServiceProxy: SpaceServiceProxyProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
func joinedParents(roomID: String) async -> Result<[SpaceRoomProxyProtocol], SpaceServiceProxyError> {
|
||||
do {
|
||||
return try await .success(spaceService.joinedParentsOfChild(childId: roomID).map(SpaceRoomProxy.init))
|
||||
} catch {
|
||||
MXLog.error("Failed to get joined parents for \(roomID): \(error)")
|
||||
return .failure(.sdkError(error))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func handleUpdates(_ updates: [SpaceListUpdate]) {
|
||||
|
||||
@@ -19,4 +19,6 @@ protocol SpaceServiceProxyProtocol {
|
||||
|
||||
func spaceRoomList(spaceID: String) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>
|
||||
func leaveSpace(spaceID: String) async -> Result<LeaveSpaceHandleProxy, SpaceServiceProxyError>
|
||||
/// Returns all the parent spaces of a room that user has joined.
|
||||
func joinedParents(roomID: String) async -> Result<[SpaceRoomProxyProtocol], SpaceServiceProxyError>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user