limit knock FF only room creation and access type changes

# Conflicts:
#	ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift
This commit is contained in:
Mauro Romito
2026-04-30 15:08:55 +02:00
committed by Stefan Ceriu
parent 272c8ca764
commit d02459d9b8
12 changed files with 10 additions and 41 deletions
@@ -238,7 +238,7 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo
case .invite: case .invite:
state.mode = .inviteRequired state.mode = .inviteRequired
case .knock, .knockRestricted: case .knock, .knockRestricted:
state.mode = appSettings.knockingEnabled ? .knockable : .joinable state.mode = .knockable
case .restricted(let rules): case .restricted(let rules):
state.mode = clientProxy.canJoinRoom(with: rules) ? .joinable : .restricted state.mode = clientProxy.canJoinRoom(with: rules) ? .joinable : .restricted
default: default:
@@ -261,7 +261,7 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo
case .invite: case .invite:
state.mode = .inviteRequired state.mode = .inviteRequired
case .knock, .knockRestricted: case .knock, .knockRestricted:
state.mode = appSettings.knockingEnabled ? .knockable : .joinable state.mode = .knockable
case .restricted(let rules): case .restricted(let rules):
state.mode = clientProxy.canJoinRoom(with: rules) ? .joinable : .restricted state.mode = clientProxy.canJoinRoom(with: rules) ? .joinable : .restricted
default: default:
@@ -404,8 +404,7 @@ struct JoinRoomScreenPreviewWrapper: Identifiable {
self.customPreviewName = customPreviewName self.customPreviewName = customPreviewName
let appSettings = AppSettings() let appSettings = AppSettings()
appSettings.knockingEnabled = true
let clientProxy = ClientProxyMock(.init(hideInviteAvatars: hideInviteAvatars)) let clientProxy = ClientProxyMock(.init(hideInviteAvatars: hideInviteAvatars))
clientProxy.canJoinRoomWithReturnValue = canJoinRoom clientProxy.canJoinRoomWithReturnValue = canJoinRoom
@@ -64,14 +64,13 @@ struct RoomDetailsScreenViewState: BindableState {
var canJoinCall = false var canJoinCall = false
var pinnedEventsActionState = RoomDetailsScreenPinnedEventsActionState.loading var pinnedEventsActionState = RoomDetailsScreenPinnedEventsActionState.loading
var knockingEnabled = false
var isKnockableRoom = false var isKnockableRoom = false
var knockRequestsCount = 0 var knockRequestsCount = 0
var reportRoomEnabled = false var reportRoomEnabled = false
var canSeeKnockingRequests: Bool { var canSeeKnockingRequests: Bool {
knockingEnabled && dmRecipientInfo == nil && isKnockableRoom && (canInviteUsers || canKickUsers || canBanUsers) dmRecipientInfo == nil && isKnockableRoom && (canInviteUsers || canKickUsers || canBanUsers)
} }
var canSeeSecurityAndPrivacy: Bool { var canSeeSecurityAndPrivacy: Bool {
@@ -71,10 +71,6 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
bindings: .init()), bindings: .init()),
mediaProvider: userSession.mediaProvider) mediaProvider: userSession.mediaProvider)
appSettings.$knockingEnabled
.weakAssign(to: \.state.knockingEnabled, on: self)
.store(in: &cancellables)
Task { Task {
state.reportRoomEnabled = await userSession.clientProxy.isReportRoomSupported state.reportRoomEnabled = await userSession.clientProxy.isReportRoomSupported
} }
@@ -374,7 +374,6 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
} }
private static func makeGenericRoomViewModel(historyVisibility: RoomHistoryVisibility) -> RoomDetailsScreenViewModel { private static func makeGenericRoomViewModel(historyVisibility: RoomHistoryVisibility) -> RoomDetailsScreenViewModel {
ServiceLocator.shared.settings.knockingEnabled = true
let knockRequests: [KnockRequestProxyMock] = [.init()] let knockRequests: [KnockRequestProxyMock] = [.init()]
let members: [RoomMemberProxyMock] = [ let members: [RoomMemberProxyMock] = [
@@ -417,7 +416,6 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
} }
private static func makeSimpleRoomViewModel() -> RoomDetailsScreenViewModel { private static func makeSimpleRoomViewModel() -> RoomDetailsScreenViewModel {
ServiceLocator.shared.settings.knockingEnabled = true
let knockRequests: [KnockRequestProxyMock] = [.init()] let knockRequests: [KnockRequestProxyMock] = [.init()]
let members: [RoomMemberProxyMock] = [ let members: [RoomMemberProxyMock] = [
@@ -73,7 +73,6 @@ struct RoomScreenViewState: BindableState {
var isDirectOneToOneRoom: Bool var isDirectOneToOneRoom: Bool
var roomThreadListEnabled = false var roomThreadListEnabled = false
var isKnockingEnabled = false
var isKnockableRoom = false var isKnockableRoom = false
var canAcceptKnocks = false var canAcceptKnocks = false
var canDeclineKnocks = false var canDeclineKnocks = false
@@ -88,8 +87,7 @@ struct RoomScreenViewState: BindableState {
} }
var shouldSeeKnockRequests: Bool { var shouldSeeKnockRequests: Bool {
isKnockingEnabled && isKnockableRoom &&
isKnockableRoom &&
!displayedKnockRequests.isEmpty && !displayedKnockRequests.isEmpty &&
(canAcceptKnocks || canDeclineKnocks || canBan) (canAcceptKnocks || canDeclineKnocks || canBan)
} }
@@ -167,10 +167,6 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
.weakAssign(to: \.state.roomThreadListEnabled, on: self) .weakAssign(to: \.state.roomThreadListEnabled, on: self)
.store(in: &cancellables) .store(in: &cancellables)
appSettings.$knockingEnabled
.weakAssign(to: \.state.isKnockingEnabled, on: self)
.store(in: &cancellables)
appSettings.$liveLocationSharingSessionsByRoomID appSettings.$liveLocationSharingSessionsByRoomID
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [weak self] sessionsByRoomID in .sink { [weak self] sessionsByRoomID in
@@ -808,7 +808,7 @@ class ClientProxy: ClientProxyProtocol {
} }
func roomDirectorySearchProxy() -> RoomDirectorySearchProxyProtocol { func roomDirectorySearchProxy() -> RoomDirectorySearchProxyProtocol {
RoomDirectorySearchProxy(roomDirectorySearch: client.roomDirectorySearch(), appSettings: appSettings) RoomDirectorySearchProxy(roomDirectorySearch: client.roomDirectorySearch())
} }
func resolveRoomAlias(_ alias: String) async -> Result<ResolvedRoomAlias, ClientProxyError> { func resolveRoomAlias(_ alias: String) async -> Result<ResolvedRoomAlias, ClientProxyError> {
@@ -1206,10 +1206,6 @@ class ClientProxy: ClientProxyProtocol {
case .invited: case .invited:
return try await .invited(InvitedRoomProxy(room: room)) return try await .invited(InvitedRoomProxy(room: room))
case .knocked: case .knocked:
guard appSettings.knockingEnabled else {
return nil
}
return try await .knocked(KnockedRoomProxy(room: room)) return try await .knocked(KnockedRoomProxy(room: room))
case .joined: case .joined:
let roomProxy = try await JoinedRoomProxy(roomListService: roomListService, let roomProxy = try await JoinedRoomProxy(roomListService: roomListService,
@@ -12,7 +12,6 @@ import MatrixRustSDK
final class RoomDirectorySearchProxy: RoomDirectorySearchProxyProtocol { final class RoomDirectorySearchProxy: RoomDirectorySearchProxyProtocol {
private let roomDirectorySearch: RoomDirectorySearchProtocol private let roomDirectorySearch: RoomDirectorySearchProtocol
private let appSettings: AppSettings
private let serialDispatchQueue = DispatchQueue(label: "io.element.elementx.room_directory_search_proxy", qos: .default) private let serialDispatchQueue = DispatchQueue(label: "io.element.elementx.room_directory_search_proxy", qos: .default)
private let resultsSubject = CurrentValueSubject<[RoomDirectorySearchResult], Never>([]) private let resultsSubject = CurrentValueSubject<[RoomDirectorySearchResult], Never>([])
@@ -32,10 +31,8 @@ final class RoomDirectorySearchProxy: RoomDirectorySearchProxyProtocol {
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
init(roomDirectorySearch: RoomDirectorySearchProtocol, init(roomDirectorySearch: RoomDirectorySearchProtocol) {
appSettings: AppSettings) {
self.roomDirectorySearch = roomDirectorySearch self.roomDirectorySearch = roomDirectorySearch
self.appSettings = appSettings
diffsPublisher diffsPublisher
.receive(on: serialDispatchQueue) .receive(on: serialDispatchQueue)
.sink { [weak self] in self?.updateResultsWithDiffs($0) } .sink { [weak self] in self?.updateResultsWithDiffs($0) }
@@ -149,6 +146,6 @@ final class RoomDirectorySearchProxy: RoomDirectorySearchProxyProtocol {
name: value.name, name: value.name,
topic: value.topic, topic: value.topic,
avatar: .room(id: value.roomId, name: value.name, avatarURL: value.avatarUrl.flatMap(URL.init(string:))), avatar: .room(id: value.roomId, name: value.name, avatarURL: value.avatarUrl.flatMap(URL.init(string:))),
canBeJoined: value.joinRule == .public || (appSettings.knockingEnabled && value.joinRule == .knock)) canBeJoined: value.joinRule == .public || value.joinRule == .knock)
} }
} }
@@ -169,8 +169,6 @@ final class JoinRoomScreenViewModelTests {
// MARK: - Helpers // MARK: - Helpers
private func setupViewModel(throwing: Bool = false, mode: TestMode = .joined) { private func setupViewModel(throwing: Bool = false, mode: TestMode = .joined) {
ServiceLocator.shared.settings.knockingEnabled = true
clientProxy = ClientProxyMock(.init()) clientProxy = ClientProxyMock(.init())
clientProxy.joinRoomViaReturnValue = throwing ? .failure(.sdkError(ClientProxyMockError.generic)) : .success(()) clientProxy.joinRoomViaReturnValue = throwing ? .failure(.sdkError(ClientProxyMockError.generic)) : .success(())
@@ -697,7 +697,6 @@ struct RoomDetailsScreenViewModelTests {
@Test @Test
mutating func knockRequestsCounter() async throws { mutating func knockRequestsCounter() async throws {
ServiceLocator.shared.settings.knockingEnabled = true
let mockedRequests: [KnockRequestProxyMock] = [.init(), .init()] let mockedRequests: [KnockRequestProxyMock] = [.init(), .init()]
roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isDirect: false, knockRequestsState: .loaded(mockedRequests), joinRule: .knock)) roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isDirect: false, knockRequestsState: .loaded(mockedRequests), joinRule: .knock))
viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
@@ -720,7 +719,6 @@ struct RoomDetailsScreenViewModelTests {
@Test @Test
mutating func knockRequestsCounterIsLoading() async throws { mutating func knockRequestsCounterIsLoading() async throws {
ServiceLocator.shared.settings.knockingEnabled = true
roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isDirect: false, knockRequestsState: .loading, joinRule: .knock)) roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isDirect: false, knockRequestsState: .loading, joinRule: .knock))
viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock, viewModel = RoomDetailsScreenViewModel(roomProxy: roomProxyMock,
userSession: UserSessionMock(.init()), userSession: UserSessionMock(.init()),
@@ -739,7 +737,6 @@ struct RoomDetailsScreenViewModelTests {
@Test @Test
mutating func knockRequestsCounterIsNotShownIfNoPermissions() async throws { mutating func knockRequestsCounterIsNotShownIfNoPermissions() async throws {
ServiceLocator.shared.settings.knockingEnabled = true
let mockedRequests: [KnockRequestProxyMock] = [.init(), .init()] let mockedRequests: [KnockRequestProxyMock] = [.init(), .init()]
roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", roomProxyMock = JoinedRoomProxyMock(.init(name: "Test",
isDirect: false, isDirect: false,
@@ -766,7 +763,6 @@ struct RoomDetailsScreenViewModelTests {
@Test @Test
mutating func knockRequestsCounterIsNotShownIfDM() async throws { mutating func knockRequestsCounterIsNotShownIfDM() async throws {
ServiceLocator.shared.settings.knockingEnabled = true
let mockedRequests: [KnockRequestProxyMock] = [.init(), .init()] let mockedRequests: [KnockRequestProxyMock] = [.init(), .init()]
let mockedMembers: [RoomMemberProxyMock] = [.mockMe, .mockAlice] let mockedMembers: [RoomMemberProxyMock] = [.mockMe, .mockAlice]
roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isDirect: true, members: mockedMembers, knockRequestsState: .loaded(mockedRequests), joinRule: .knock)) roomProxyMock = JoinedRoomProxyMock(.init(name: "Test", isDirect: true, members: mockedMembers, knockRequestsState: .loaded(mockedRequests), joinRule: .knock))
@@ -341,7 +341,6 @@ final class RoomScreenViewModelTests {
@Test @Test
func knockRequestBanner() async throws { func knockRequestBanner() async throws {
ServiceLocator.shared.settings.knockingEnabled = true
let roomProxyMock = JoinedRoomProxyMock(.init(knockRequestsState: .loaded([KnockRequestProxyMock(.init(eventID: "1", userID: "@alice:matrix.org", displayName: "Alice", reason: "Hello World!")), let roomProxyMock = JoinedRoomProxyMock(.init(knockRequestsState: .loaded([KnockRequestProxyMock(.init(eventID: "1", userID: "@alice:matrix.org", displayName: "Alice", reason: "Hello World!")),
// This one should be filtered // This one should be filtered
KnockRequestProxyMock(.init(eventID: "2", userID: "@bob:matrix.org", isSeen: true))]), KnockRequestProxyMock(.init(eventID: "2", userID: "@bob:matrix.org", isSeen: true))]),
@@ -376,7 +375,6 @@ final class RoomScreenViewModelTests {
@Test @Test
func knockRequestBannerMarkAsSeen() async throws { func knockRequestBannerMarkAsSeen() async throws {
ServiceLocator.shared.settings.knockingEnabled = true
let roomProxyMock = JoinedRoomProxyMock(.init(knockRequestsState: .loaded([KnockRequestProxyMock(.init(eventID: "1", userID: "@alice:matrix.org", displayName: "Alice", reason: "Hello World!")), let roomProxyMock = JoinedRoomProxyMock(.init(knockRequestsState: .loaded([KnockRequestProxyMock(.init(eventID: "1", userID: "@alice:matrix.org", displayName: "Alice", reason: "Hello World!")),
// This one should be filtered // This one should be filtered
KnockRequestProxyMock(.init(eventID: "2", userID: "@bob:matrix.org"))]), KnockRequestProxyMock(.init(eventID: "2", userID: "@bob:matrix.org"))]),
@@ -408,7 +406,6 @@ final class RoomScreenViewModelTests {
@Test @Test
func loadingKnockRequests() async throws { func loadingKnockRequests() async throws {
ServiceLocator.shared.settings.knockingEnabled = true
let roomProxyMock = JoinedRoomProxyMock(.init(knockRequestsState: .loading, let roomProxyMock = JoinedRoomProxyMock(.init(knockRequestsState: .loading,
joinRule: .knock)) joinRule: .knock))
let viewModel = RoomScreenViewModel(userSession: UserSessionMock(.init()), let viewModel = RoomScreenViewModel(userSession: UserSessionMock(.init()),
@@ -428,7 +425,6 @@ final class RoomScreenViewModelTests {
@Test @Test
func knockRequestsBannerDoesNotAppearIfUserHasNoPermission() async throws { func knockRequestsBannerDoesNotAppearIfUserHasNoPermission() async throws {
ServiceLocator.shared.settings.knockingEnabled = true
let roomProxyMock = JoinedRoomProxyMock(.init(knockRequestsState: .loaded([KnockRequestProxyMock(.init(eventID: "1", userID: "@alice:matrix.org", displayName: "Alice", reason: "Hello World!"))]), let roomProxyMock = JoinedRoomProxyMock(.init(knockRequestsState: .loaded([KnockRequestProxyMock(.init(eventID: "1", userID: "@alice:matrix.org", displayName: "Alice", reason: "Hello World!"))]),
joinRule: .knock, joinRule: .knock,
powerLevelsConfiguration: .init(canUserInvite: false))) powerLevelsConfiguration: .init(canUserInvite: false)))