remove space FFs

This commit is contained in:
Mauro Romito
2026-02-26 15:45:07 +01:00
committed by Mauro
parent 737b907d26
commit ce97fec477
18 changed files with 30 additions and 100 deletions
@@ -25,7 +25,6 @@ import SwiftUI
let icon: KeyPath<CompoundIcons, Image> let icon: KeyPath<CompoundIcons, Image>
let selectedIcon: KeyPath<CompoundIcons, Image> let selectedIcon: KeyPath<CompoundIcons, Image>
var badgeCount = 0 var badgeCount = 0
var barVisibilityOverride: Visibility?
/// Provide the tab's split coordinator in here to have the tab bar automatically hidden /// Provide the tab's split coordinator in here to have the tab bar automatically hidden
/// when pushing a child into the split view's details on iPhone/compact iPad. /// when pushing a child into the split view's details on iPhone/compact iPad.
@@ -39,9 +38,7 @@ import SwiftUI
} }
func barVisibility(in horizontalSizeClass: UserInterfaceSizeClass?) -> Visibility { func barVisibility(in horizontalSizeClass: UserInterfaceSizeClass?) -> Visibility {
if let barVisibilityOverride { if horizontalSizeClass == .compact, navigationSplitCoordinator?.detailCoordinator != nil {
barVisibilityOverride
} else if horizontalSizeClass == .compact, navigationSplitCoordinator?.detailCoordinator != nil {
// Whilst we support pushing screens on the stack in the sidebarCoordinator, in practice // Whilst we support pushing screens on the stack in the sidebarCoordinator, in practice
// we never do that, so simply checking that the detailCoordinator exists is enough. // we never do that, so simply checking that the detailCoordinator exists is enough.
.hidden .hidden
@@ -77,11 +77,6 @@ final class AppSettings {
case focusEventOnNotificationTap case focusEventOnNotificationTap
case linkNewDeviceEnabled case linkNewDeviceEnabled
// Spaces
case spaceFiltersEnabled
case spaceSettingsEnabled
case createSpaceEnabled
case voiceMessagePlaybackSpeed case voiceMessagePlaybackSpeed
// Doug's tweaks 🔧 // Doug's tweaks 🔧
@@ -404,16 +399,6 @@ final class AppSettings {
// MARK: - Feature Flags // MARK: - Feature Flags
/// Spaces
@UserPreference(key: UserDefaultsKeys.spaceSettingsEnabled, defaultValue: true, storageType: .volatile)
var spaceSettingsEnabled
@UserPreference(key: UserDefaultsKeys.createSpaceEnabled, defaultValue: true, storageType: .volatile)
var createSpaceEnabled
@UserPreference(key: UserDefaultsKeys.spaceFiltersEnabled, defaultValue: true, storageType: .volatile)
var spaceFiltersEnabled
/// Others /// Others
@UserPreference(key: UserDefaultsKeys.publicSearchEnabled, defaultValue: false, storageType: .userDefaults(store)) @UserPreference(key: UserDefaultsKeys.publicSearchEnabled, defaultValue: false, storageType: .userDefaults(store))
var publicSearchEnabled var publicSearchEnabled
@@ -150,7 +150,7 @@ class StartChatFlowCoordinator: FlowCoordinatorProtocol {
stateMachine.addRoutes(event: .createRoom(isSpace: false), transitions: [.startChat => .createRoom]) { [weak self] context in stateMachine.addRoutes(event: .createRoom(isSpace: false), transitions: [.startChat => .createRoom]) { [weak self] context in
guard let self, context.fromState == .startChat else { return } // Required check because the event is used in another route. guard let self, context.fromState == .startChat else { return } // Required check because the event is used in another route.
presentCreateRoomScreen(isSpace: false, presentCreateRoomScreen(isSpace: false,
spaceSelectionMode: flowParameters.appSettings.createSpaceEnabled ? .editableSpacesList(preSelectedSpace: nil) : .none, spaceSelectionMode: .editableSpacesList(preSelectedSpace: nil),
isRoot: false) isRoot: false)
} }
stateMachine.addRoutes(event: .dismissedCreateRoom, transitions: [.createRoom => .startChat]) { [weak self] _ in stateMachine.addRoutes(event: .dismissedCreateRoom, transitions: [.createRoom => .startChat]) { [weak self] _ in
@@ -288,14 +288,6 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
} }
} }
.store(in: &cancellables) .store(in: &cancellables)
userSession.clientProxy.spaceService.topLevelSpacesPublisher
.combineLatest(flowParameters.appSettings.$createSpaceEnabled)
.map { topLevelSpaces, isCreateSpaceEnabled in
!isCreateSpaceEnabled && topLevelSpaces.isEmpty ? .hidden : nil
}
.weakAssign(to: \.chatsTabDetails.barVisibilityOverride, on: self)
.store(in: &cancellables)
} }
// MARK: - Onboarding // MARK: - Onboarding
@@ -109,9 +109,7 @@ struct HomeScreenViewState: BindableState {
var hideInviteAvatars = false var hideInviteAvatars = false
var reportRoomEnabled = false var reportRoomEnabled = false
var spaceFiltersEnabled = false
var shouldShowSpaceFilters = false var shouldShowSpaceFilters = false
var selectedSpaceFilter: SpaceServiceFilter? var selectedSpaceFilter: SpaceServiceFilter?
@@ -46,7 +46,6 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
roomSummaryProvider = userSession.clientProxy.roomSummaryProvider roomSummaryProvider = userSession.clientProxy.roomSummaryProvider
super.init(initialViewState: .init(userID: userSession.clientProxy.userID, super.init(initialViewState: .init(userID: userSession.clientProxy.userID,
spaceFiltersEnabled: appSettings.spaceFiltersEnabled,
bindings: .init(filtersState: .init(appSettings: appSettings))), bindings: .init(filtersState: .init(appSettings: appSettings))),
mediaProvider: userSession.mediaProvider) mediaProvider: userSession.mediaProvider)
@@ -132,11 +131,6 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
} }
.store(in: &cancellables) .store(in: &cancellables)
appSettings.$spaceFiltersEnabled
.receive(on: DispatchQueue.main)
.weakAssign(to: \.state.spaceFiltersEnabled, on: self)
.store(in: &cancellables)
userSession.clientProxy.hideInviteAvatarsPublisher userSession.clientProxy.hideInviteAvatarsPublisher
.removeDuplicates() .removeDuplicates()
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
@@ -66,7 +66,7 @@ struct HomeScreen: View {
} }
} }
if context.viewState.spaceFiltersEnabled, context.viewState.shouldShowSpaceFilters { if context.viewState.shouldShowSpaceFilters {
if #available(iOS 26, *) { if #available(iOS 26, *) {
ToolbarSpacer(.fixed, placement: .primaryAction) ToolbarSpacer(.fixed, placement: .primaryAction)
} }
@@ -23,7 +23,6 @@ struct SecurityAndPrivacyScreenViewState: BindableState {
var canonicalAlias: String? var canonicalAlias: String?
var isKnockingEnabled: Bool var isKnockingEnabled: Bool
var isSpaceSettingsEnabled: Bool
var isSpace: Bool var isSpace: Bool
var canEditAddress = false var canEditAddress = false
@@ -65,7 +64,7 @@ struct SecurityAndPrivacyScreenViewState: BindableState {
} }
var isSpaceMembersOptionSelectable: Bool { var isSpaceMembersOptionSelectable: Bool {
isSpaceSettingsEnabled && selectableSpacesCount > 0 selectableSpacesCount > 0
} }
var isAskToJoinWithSpaceMembersOptionAvailable: Bool { var isAskToJoinWithSpaceMembersOptionAvailable: Bool {
@@ -160,12 +159,10 @@ struct SecurityAndPrivacyScreenViewState: BindableState {
historyVisibility: SecurityAndPrivacyHistoryVisibility, historyVisibility: SecurityAndPrivacyHistoryVisibility,
isSpace: Bool, isSpace: Bool,
isKnockingEnabled: Bool, isKnockingEnabled: Bool,
isSpaceSettingsEnabled: Bool,
historySharingDetailsURL: URL) { historySharingDetailsURL: URL) {
self.serverName = serverName self.serverName = serverName
self.isKnockingEnabled = isKnockingEnabled self.isKnockingEnabled = isKnockingEnabled
self.isSpace = isSpace self.isSpace = isSpace
self.isSpaceSettingsEnabled = isSpaceSettingsEnabled
let settings = SecurityAndPrivacySettings(accessType: accessType, let settings = SecurityAndPrivacySettings(accessType: accessType,
isEncryptionEnabled: isEncryptionEnabled, isEncryptionEnabled: isEncryptionEnabled,
@@ -38,7 +38,6 @@ class SecurityAndPrivacyScreenViewModel: SecurityAndPrivacyScreenViewModelType,
historyVisibility: roomProxy.infoPublisher.value.historyVisibility.toSecurityAndPrivacyHistoryVisibility, historyVisibility: roomProxy.infoPublisher.value.historyVisibility.toSecurityAndPrivacyHistoryVisibility,
isSpace: roomProxy.infoPublisher.value.isSpace, isSpace: roomProxy.infoPublisher.value.isSpace,
isKnockingEnabled: appSettings.knockingEnabled, isKnockingEnabled: appSettings.knockingEnabled,
isSpaceSettingsEnabled: appSettings.spaceSettingsEnabled,
historySharingDetailsURL: appSettings.historySharingDetailsURL)) historySharingDetailsURL: appSettings.historySharingDetailsURL))
if let powerLevels = roomProxy.infoPublisher.value.powerLevels { if let powerLevels = roomProxy.infoPublisher.value.powerLevels {
@@ -153,10 +152,6 @@ class SecurityAndPrivacyScreenViewModel: SecurityAndPrivacyScreenViewModelType,
appSettings.$knockingEnabled appSettings.$knockingEnabled
.weakAssign(to: \.state.isKnockingEnabled, on: self) .weakAssign(to: \.state.isKnockingEnabled, on: self)
.store(in: &cancellables) .store(in: &cancellables)
appSettings.$spaceSettingsEnabled
.weakAssign(to: \.state.isSpaceSettingsEnabled, on: self)
.store(in: &cancellables)
} }
private func setupPermissions(powerLevels: RoomPowerLevelsProxyProtocol) { private func setupPermissions(powerLevels: RoomPowerLevelsProxyProtocol) {
@@ -263,7 +263,7 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
static let singleSpaceMembersViewModel = { static let singleSpaceMembersViewModel = {
AppSettings.resetAllSettings() AppSettings.resetAllSettings()
let appSettings = AppSettings() let appSettings = AppSettings()
appSettings.spaceSettingsEnabled = true
let space = [SpaceServiceRoom].mockSingleRoom[0] let space = [SpaceServiceRoom].mockSingleRoom[0]
return SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false, return SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false,
@@ -280,7 +280,7 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
static let multipleSpacesMembersViewModel = { static let multipleSpacesMembersViewModel = {
AppSettings.resetAllSettings() AppSettings.resetAllSettings()
let appSettings = AppSettings() let appSettings = AppSettings()
appSettings.spaceSettingsEnabled = true
let spaces = [SpaceServiceRoom].mockJoinedSpaces let spaces = [SpaceServiceRoom].mockJoinedSpaces
return SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false, return SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false,
@@ -312,8 +312,8 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
static let singleAskToJoinSpaceMembersViewModel = { static let singleAskToJoinSpaceMembersViewModel = {
AppSettings.resetAllSettings() AppSettings.resetAllSettings()
let appSettings = AppSettings() let appSettings = AppSettings()
appSettings.spaceSettingsEnabled = true
appSettings.knockingEnabled = true appSettings.knockingEnabled = true
let space = [SpaceServiceRoom].mockSingleRoom[0] let space = [SpaceServiceRoom].mockSingleRoom[0]
return SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false, return SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false,
@@ -330,8 +330,8 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
static let multipleAskToJoinSpacesMembersViewModel = { static let multipleAskToJoinSpacesMembersViewModel = {
AppSettings.resetAllSettings() AppSettings.resetAllSettings()
let appSettings = AppSettings() let appSettings = AppSettings()
appSettings.spaceSettingsEnabled = true
appSettings.knockingEnabled = true appSettings.knockingEnabled = true
let spaces = [SpaceServiceRoom].mockJoinedSpaces let spaces = [SpaceServiceRoom].mockJoinedSpaces
return SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false, return SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false,
@@ -36,7 +36,6 @@ struct SpaceScreenViewState: BindableState {
var canEditRolesAndPermissions = false var canEditRolesAndPermissions = false
var canEditSecurityAndPrivacy = false var canEditSecurityAndPrivacy = false
var canEditChildren = false var canEditChildren = false
var canCreateRoom = false
var editMode: EditMode = .inactive var editMode: EditMode = .inactive
var editModeSelectedIDs: Set<String> = [] var editModeSelectedIDs: Set<String> = []
@@ -75,10 +75,6 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
.weakAssign(to: \.state.selectedSpaceRoomID, on: self) .weakAssign(to: \.state.selectedSpaceRoomID, on: self)
.store(in: &cancellables) .store(in: &cancellables)
appSettings.$createSpaceEnabled
.weakAssign(to: \.state.canCreateRoom, on: self)
.store(in: &cancellables)
Task { Task {
if case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(spaceRoomListProxy.id) { if case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(spaceRoomListProxy.id) {
// Required to listen for membership updates in the members flow // Required to listen for membership updates in the members flow
@@ -88,11 +84,10 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
state.permalink = permalinkURL state.permalink = permalinkURL
} }
appSettings.$spaceSettingsEnabled roomProxy.infoPublisher
.combineLatest(roomProxy.infoPublisher) .sink { [weak self] roomInfo in
.sink { [weak self] isEnabled, roomInfo in
guard let self else { return } guard let self else { return }
guard isEnabled, let powerLevels = roomInfo.powerLevels else { guard let powerLevels = roomInfo.powerLevels else {
state.canEditBaseInfo = false state.canEditBaseInfo = false
state.canEditRolesAndPermissions = false state.canEditRolesAndPermissions = false
state.canEditSecurityAndPrivacy = false state.canEditSecurityAndPrivacy = false
@@ -247,7 +242,7 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
} }
let leaveSpaceViewModel = LeaveSpaceViewModel(spaceName: state.space.name, let leaveSpaceViewModel = LeaveSpaceViewModel(spaceName: state.space.name,
canEditRolesAndPermissions: appSettings.spaceSettingsEnabled && state.canEditRolesAndPermissions, canEditRolesAndPermissions: state.canEditRolesAndPermissions,
leaveHandle: leaveHandle, leaveHandle: leaveHandle,
userIndicatorController: userIndicatorController, userIndicatorController: userIndicatorController,
mediaProvider: mediaProvider) mediaProvider: mediaProvider)
@@ -78,12 +78,10 @@ struct SpaceScreen: View {
} }
.buttonStyle(.compound(.primary)) .buttonStyle(.compound(.primary))
if context.viewState.canCreateRoom { Button(L10n.actionCreateRoom) {
Button(L10n.actionCreateRoom) { context.send(viewAction: .createChildRoom)
context.send(viewAction: .createChildRoom)
}
.buttonStyle(.compound(.secondary))
} }
.buttonStyle(.compound(.secondary))
} }
.padding(.horizontal, 16) .padding(.horizontal, 16)
} }
@@ -128,11 +126,9 @@ struct SpaceScreen: View {
Menu { Menu {
if context.viewState.canEditChildren { if context.viewState.canEditChildren {
Section { Section {
if context.viewState.canCreateRoom { Button { context.send(viewAction: .createChildRoom) } label: {
Button { context.send(viewAction: .createChildRoom) } label: { Label(L10n.actionCreateRoom, icon: \.plus)
Label(L10n.actionCreateRoom, icon: \.plus) .accessibilityIdentifier(A11yIdentifiers.spaceScreen.createRoom)
}
.accessibilityIdentifier(A11yIdentifiers.spaceScreen.createRoom)
} }
Button { context.send(viewAction: .addExistingRooms) } label: { Button { context.send(viewAction: .addExistingRooms) } label: {
@@ -207,16 +203,10 @@ struct SpaceScreen_Previews: PreviewProvider, TestablePreview {
SpaceScreen(context: newSpaceViewModel.context) SpaceScreen(context: newSpaceViewModel.context)
} }
.previewDisplayName("New Space") .previewDisplayName("New Space")
.snapshotPreferences(expect: newSpaceViewModel.context.observe(\.viewState).map { .snapshotPreferences(expect: newSpaceViewModel.context.observe(\.viewState).map(\.canEditChildren))
$0.canCreateRoom && $0.canEditChildren
})
} }
static func makeViewModel(isManagingRooms: Bool = false, isNewSpace: Bool = false) -> SpaceScreenViewModel { static func makeViewModel(isManagingRooms: Bool = false, isNewSpace: Bool = false) -> SpaceScreenViewModel {
let appSettings = AppSettings()
appSettings.spaceSettingsEnabled = true
appSettings.createSpaceEnabled = true
let spaceServiceRoom = SpaceServiceRoom.mock(id: "!eng-space:matrix.org", let spaceServiceRoom = SpaceServiceRoom.mock(id: "!eng-space:matrix.org",
name: "Engineering Team", name: "Engineering Team",
isSpace: true, isSpace: true,
@@ -239,7 +229,7 @@ struct SpaceScreen_Previews: PreviewProvider, TestablePreview {
spaceServiceProxy: SpaceServiceProxyMock(.init()), spaceServiceProxy: SpaceServiceProxyMock(.init()),
selectedSpaceRoomPublisher: .init(nil), selectedSpaceRoomPublisher: .init(nil),
userSession: userSession, userSession: userSession,
appSettings: appSettings, appSettings: AppSettings(),
userIndicatorController: UserIndicatorControllerMock()) userIndicatorController: UserIndicatorControllerMock())
if isManagingRooms { if isManagingRooms {
@@ -21,9 +21,7 @@ struct SpacesScreenViewState: BindableState {
var topLevelSpaces: [SpaceServiceRoom] var topLevelSpaces: [SpaceServiceRoom]
var selectedSpaceID: String? var selectedSpaceID: String?
var isCreateSpaceEnabled: Bool
var bindings: SpacesScreenViewStateBindings var bindings: SpacesScreenViewStateBindings
} }
@@ -31,7 +31,6 @@ class SpacesScreenViewModel: SpacesScreenViewModelType, SpacesScreenViewModelPro
super.init(initialViewState: SpacesScreenViewState(userID: userSession.clientProxy.userID, super.init(initialViewState: SpacesScreenViewState(userID: userSession.clientProxy.userID,
topLevelSpaces: spaceServiceProxy.topLevelSpacesPublisher.value, topLevelSpaces: spaceServiceProxy.topLevelSpacesPublisher.value,
isCreateSpaceEnabled: appSettings.createSpaceEnabled,
bindings: .init()), bindings: .init()),
mediaProvider: userSession.mediaProvider) mediaProvider: userSession.mediaProvider)
@@ -53,10 +52,6 @@ class SpacesScreenViewModel: SpacesScreenViewModelType, SpacesScreenViewModelPro
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.weakAssign(to: \.state.userDisplayName, on: self) .weakAssign(to: \.state.userDisplayName, on: self)
.store(in: &cancellables) .store(in: &cancellables)
appSettings.$createSpaceEnabled
.weakAssign(to: \.state.isCreateSpaceEnabled, on: self)
.store(in: &cancellables)
} }
// MARK: - Public // MARK: - Public
@@ -27,7 +27,7 @@ struct SpacesScreen: View {
@ViewBuilder @ViewBuilder
private var mainContent: some View { private var mainContent: some View {
if context.viewState.isCreateSpaceEnabled, context.viewState.topLevelSpaces.isEmpty { if context.viewState.topLevelSpaces.isEmpty {
emptyState emptyState
} else { } else {
ScrollView { ScrollView {
@@ -118,16 +118,14 @@ struct SpacesScreen: View {
} }
.backportSharedBackgroundVisibility(.hidden) .backportSharedBackgroundVisibility(.hidden)
if context.viewState.isCreateSpaceEnabled { ToolbarItem(placement: .navigationBarTrailing) {
ToolbarItem(placement: .navigationBarTrailing) { Button {
Button { context.send(viewAction: .createSpace)
context.send(viewAction: .createSpace) } label: {
} label: { CompoundIcon(\.plus)
CompoundIcon(\.plus) .accessibilityHidden(true)
.accessibilityHidden(true)
}
.accessibilityLabel(L10n.actionCreateSpace)
} }
.accessibilityLabel(L10n.actionCreateSpace)
} }
} }
} }
@@ -152,7 +150,6 @@ struct SpacesScreen_Previews: PreviewProvider, TestablePreview {
static func makeViewModel(isEmpty: Bool = false) -> SpacesScreenViewModel { static func makeViewModel(isEmpty: Bool = false) -> SpacesScreenViewModel {
AppSettings.resetAllSettings() AppSettings.resetAllSettings()
let appSettings = AppSettings() let appSettings = AppSettings()
appSettings.createSpaceEnabled = true
appSettings.hasSeenSpacesAnnouncement = true appSettings.hasSeenSpacesAnnouncement = true
let clientProxy = ClientProxyMock(.init()) let clientProxy = ClientProxyMock(.init())
@@ -588,7 +588,6 @@ class MockScreen: Identifiable {
appSettings.hasRunNotificationPermissionsOnboarding = true appSettings.hasRunNotificationPermissionsOnboarding = true
appSettings.analyticsConsentState = .optedOut appSettings.analyticsConsentState = .optedOut
appSettings.hasSeenSpacesAnnouncement = true appSettings.hasSeenSpacesAnnouncement = true
appSettings.spaceSettingsEnabled = true
let roomSummaries: [RoomSummary] = if id == .userSessionSpacesFlow { let roomSummaries: [RoomSummary] = if id == .userSessionSpacesFlow {
[[RoomSummary].mockSpaceInvites[0]] + .mockRooms [[RoomSummary].mockSpaceInvites[0]] + .mockRooms
@@ -446,7 +446,6 @@ final class SecurityAndPrivacyScreenViewModelTests {
topLevelSpaces: [SpaceServiceRoom] = [], topLevelSpaces: [SpaceServiceRoom] = [],
joinRule: ElementX.JoinRule) { joinRule: ElementX.JoinRule) {
let appSettings = AppSettings() let appSettings = AppSettings()
appSettings.spaceSettingsEnabled = true
appSettings.knockingEnabled = true appSettings.knockingEnabled = true
roomProxy = JoinedRoomProxyMock(.init(isEncrypted: false, roomProxy = JoinedRoomProxyMock(.init(isEncrypted: false,
canonicalAlias: "#room:matrix.org", canonicalAlias: "#room:matrix.org",