diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index 66a4ef783..1f97ab1f3 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -47,6 +47,7 @@ final class AppSettings { case pinningEnabled case enableOnlySignedDeviceIsolationMode case identityPinningViolationNotificationsEnabled + case knockingEnabled } private static var suiteName: String = InfoPlistReader.main.appGroupIdentifier @@ -282,6 +283,9 @@ final class AppSettings { @UserPreference(key: UserDefaultsKeys.identityPinningViolationNotificationsEnabled, defaultValue: isDevelopmentBuild, storageType: .userDefaults(store)) var identityPinningViolationNotificationsEnabled + + @UserPreference(key: UserDefaultsKeys.knockingEnabled, defaultValue: false, storageType: .userDefaults(store)) + var knockingEnabled #endif diff --git a/ElementX/Sources/Mocks/ClientProxyMock.swift b/ElementX/Sources/Mocks/ClientProxyMock.swift index 825588e47..507a21b0a 100644 --- a/ElementX/Sources/Mocks/ClientProxyMock.swift +++ b/ElementX/Sources/Mocks/ClientProxyMock.swift @@ -50,7 +50,7 @@ extension ClientProxyMock { canDeactivateAccount = false directRoomForUserIDReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) createDirectRoomWithExpectedRoomNameReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) - createRoomNameTopicIsRoomPrivateUserIDsAvatarURLReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) + createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) uploadMediaReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) loadUserDisplayNameReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) setUserDisplayNameReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index c9f2da643..db66e04c9 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -2627,15 +2627,15 @@ class ClientProxyMock: ClientProxyProtocol { } //MARK: - createRoom - var createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingCallsCount = 0 - var createRoomNameTopicIsRoomPrivateUserIDsAvatarURLCallsCount: Int { + var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingCallsCount = 0 + var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLCallsCount: Int { get { if Thread.isMainThread { - return createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingCallsCount + return createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingCallsCount + returnValue = createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingCallsCount } return returnValue! @@ -2643,29 +2643,29 @@ class ClientProxyMock: ClientProxyProtocol { } set { if Thread.isMainThread { - createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingCallsCount = newValue + createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingCallsCount = newValue + createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingCallsCount = newValue } } } } - var createRoomNameTopicIsRoomPrivateUserIDsAvatarURLCalled: Bool { - return createRoomNameTopicIsRoomPrivateUserIDsAvatarURLCallsCount > 0 + var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLCalled: Bool { + return createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLCallsCount > 0 } - var createRoomNameTopicIsRoomPrivateUserIDsAvatarURLReceivedArguments: (name: String, topic: String?, isRoomPrivate: Bool, userIDs: [String], avatarURL: URL?)? - var createRoomNameTopicIsRoomPrivateUserIDsAvatarURLReceivedInvocations: [(name: String, topic: String?, isRoomPrivate: Bool, userIDs: [String], avatarURL: URL?)] = [] + var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLReceivedArguments: (name: String, topic: String?, isRoomPrivate: Bool, isKnockingOnly: Bool, userIDs: [String], avatarURL: URL?)? + var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLReceivedInvocations: [(name: String, topic: String?, isRoomPrivate: Bool, isKnockingOnly: Bool, userIDs: [String], avatarURL: URL?)] = [] - var createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingReturnValue: Result! - var createRoomNameTopicIsRoomPrivateUserIDsAvatarURLReturnValue: Result! { + var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingReturnValue: Result! + var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLReturnValue: Result! { get { if Thread.isMainThread { - return createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingReturnValue + return createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingReturnValue } else { var returnValue: Result? = nil DispatchQueue.main.sync { - returnValue = createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingReturnValue + returnValue = createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingReturnValue } return returnValue! @@ -2673,26 +2673,26 @@ class ClientProxyMock: ClientProxyProtocol { } set { if Thread.isMainThread { - createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingReturnValue = newValue + createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - createRoomNameTopicIsRoomPrivateUserIDsAvatarURLUnderlyingReturnValue = newValue + createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLUnderlyingReturnValue = newValue } } } } - var createRoomNameTopicIsRoomPrivateUserIDsAvatarURLClosure: ((String, String?, Bool, [String], URL?) async -> Result)? + var createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLClosure: ((String, String?, Bool, Bool, [String], URL?) async -> Result)? - func createRoom(name: String, topic: String?, isRoomPrivate: Bool, userIDs: [String], avatarURL: URL?) async -> Result { - createRoomNameTopicIsRoomPrivateUserIDsAvatarURLCallsCount += 1 - createRoomNameTopicIsRoomPrivateUserIDsAvatarURLReceivedArguments = (name: name, topic: topic, isRoomPrivate: isRoomPrivate, userIDs: userIDs, avatarURL: avatarURL) + func createRoom(name: String, topic: String?, isRoomPrivate: Bool, isKnockingOnly: Bool, userIDs: [String], avatarURL: URL?) async -> Result { + createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLCallsCount += 1 + createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLReceivedArguments = (name: name, topic: topic, isRoomPrivate: isRoomPrivate, isKnockingOnly: isKnockingOnly, userIDs: userIDs, avatarURL: avatarURL) DispatchQueue.main.async { - self.createRoomNameTopicIsRoomPrivateUserIDsAvatarURLReceivedInvocations.append((name: name, topic: topic, isRoomPrivate: isRoomPrivate, userIDs: userIDs, avatarURL: avatarURL)) + self.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLReceivedInvocations.append((name: name, topic: topic, isRoomPrivate: isRoomPrivate, isKnockingOnly: isKnockingOnly, userIDs: userIDs, avatarURL: avatarURL)) } - if let createRoomNameTopicIsRoomPrivateUserIDsAvatarURLClosure = createRoomNameTopicIsRoomPrivateUserIDsAvatarURLClosure { - return await createRoomNameTopicIsRoomPrivateUserIDsAvatarURLClosure(name, topic, isRoomPrivate, userIDs, avatarURL) + if let createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLClosure = createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLClosure { + return await createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLClosure(name, topic, isRoomPrivate, isKnockingOnly, userIDs, avatarURL) } else { - return createRoomNameTopicIsRoomPrivateUserIDsAvatarURLReturnValue + return createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLReturnValue } } //MARK: - joinRoom diff --git a/ElementX/Sources/Screens/CreateRoom/CreateRoomCoordinator.swift b/ElementX/Sources/Screens/CreateRoom/CreateRoomCoordinator.swift index da965f2e2..a37163f97 100644 --- a/ElementX/Sources/Screens/CreateRoom/CreateRoomCoordinator.swift +++ b/ElementX/Sources/Screens/CreateRoom/CreateRoomCoordinator.swift @@ -37,7 +37,8 @@ final class CreateRoomCoordinator: CoordinatorProtocol { createRoomParameters: parameters.createRoomParameters, selectedUsers: parameters.selectedUsers, analytics: ServiceLocator.shared.analytics, - userIndicatorController: parameters.userIndicatorController) + userIndicatorController: parameters.userIndicatorController, + appSettings: ServiceLocator.shared.settings) } func start() { diff --git a/ElementX/Sources/Screens/CreateRoom/CreateRoomModels.swift b/ElementX/Sources/Screens/CreateRoom/CreateRoomModels.swift index 87ef365a1..44cefd052 100644 --- a/ElementX/Sources/Screens/CreateRoom/CreateRoomModels.swift +++ b/ElementX/Sources/Screens/CreateRoom/CreateRoomModels.swift @@ -25,6 +25,7 @@ enum CreateRoomViewModelAction { } struct CreateRoomViewState: BindableState { + let isKnockingFeatureEnabled: Bool var selectedUsers: [UserProfileProxy] var bindings: CreateRoomViewStateBindings var avatarURL: URL? @@ -37,6 +38,7 @@ struct CreateRoomViewStateBindings { var roomName: String var roomTopic: String var isRoomPrivate: Bool + var isKnockingOnly = false var showAttachmentConfirmationDialog = false /// Information describing the currently displayed alert. diff --git a/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift b/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift index e8d4e2da9..39fde2fcd 100644 --- a/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift +++ b/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift @@ -26,7 +26,8 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol createRoomParameters: CurrentValuePublisher, selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never>, analytics: AnalyticsService, - userIndicatorController: UserIndicatorControllerProtocol) { + userIndicatorController: UserIndicatorControllerProtocol, + appSettings: AppSettings) { let parameters = createRoomParameters.value self.userSession = userSession @@ -36,7 +37,7 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol let bindings = CreateRoomViewStateBindings(roomName: parameters.name, roomTopic: parameters.topic, isRoomPrivate: parameters.isRoomPrivate) - super.init(initialViewState: CreateRoomViewState(selectedUsers: selectedUsers.value, bindings: bindings), mediaProvider: userSession.mediaProvider) + super.init(initialViewState: CreateRoomViewState(isKnockingFeatureEnabled: appSettings.knockingEnabled, selectedUsers: selectedUsers.value, bindings: bindings), mediaProvider: userSession.mediaProvider) createRoomParameters .map(\.avatarImageMedia) @@ -92,21 +93,28 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol old.roomName == new.roomName && old.roomTopic == new.roomTopic && old.isRoomPrivate == new.isRoomPrivate } .sink { [weak self] bindings in - guard let self else { return } - createRoomParameters.name = bindings.roomName - createRoomParameters.topic = bindings.roomTopic - createRoomParameters.isRoomPrivate = bindings.isRoomPrivate + guard let self = self else { return } + updateParameters(bindings: bindings) actionsSubject.send(.updateDetails(createRoomParameters)) } .store(in: &cancellables) } + private func updateParameters(bindings: CreateRoomViewStateBindings) { + createRoomParameters.name = bindings.roomName + createRoomParameters.topic = bindings.roomTopic + createRoomParameters.isRoomPrivate = bindings.isRoomPrivate + createRoomParameters.isKnockingOnly = bindings.isKnockingOnly + } + private func createRoom() async { defer { hideLoadingIndicator() } showLoadingIndicator() + // Since the parameters are throttled, we need to make sure that the latest values are used + updateParameters(bindings: state.bindings) let avatarURL: URL? if let media = createRoomParameters.avatarImageMedia { switch await userSession.clientProxy.uploadMedia(media) { @@ -136,6 +144,8 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol switch await userSession.clientProxy.createRoom(name: createRoomParameters.name, topic: createRoomParameters.topic, isRoomPrivate: createRoomParameters.isRoomPrivate, + // As of right now we don't want to make private rooms with the knock rule + isKnockingOnly: createRoomParameters.isRoomPrivate ? false : createRoomParameters.isKnockingOnly, userIDs: state.selectedUsers.map(\.userID), avatarURL: avatarURL) { case .success(let roomId): diff --git a/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift b/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift index 32eb00b9e..726cf99f0 100644 --- a/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift +++ b/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift @@ -22,6 +22,10 @@ struct CreateRoomScreen: View { roomSection topicSection securitySection + if context.viewState.isKnockingFeatureEnabled, + !context.isRoomPrivate { + roomAccessSection + } } .compoundList() .track(screen: .CreateRoom) @@ -151,6 +155,20 @@ struct CreateRoomScreen: View { } } + private var roomAccessSection: some View { + Section { + ListRow(label: .plain(title: L10n.screenCreateRoomAccessSectionAnyoneOptionTitle, + description: L10n.screenCreateRoomAccessSectionAnyoneOptionDescription), + kind: .selection(isSelected: !context.isKnockingOnly) { context.isKnockingOnly = false }) + ListRow(label: .plain(title: L10n.screenCreateRoomAccessSectionKnockingOptionTitle, + description: L10n.screenCreateRoomAccessSectionKnockingOptionDescription), + kind: .selection(isSelected: context.isKnockingOnly) { context.isKnockingOnly = true }) + } header: { + Text(L10n.screenCreateRoomAccessSectionHeader.uppercased()) + .compoundListSectionHeader() + } + } + private var toolbar: some ToolbarContent { ToolbarItem(placement: .confirmationAction) { Button(L10n.actionCreate) { @@ -174,7 +192,8 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview { createRoomParameters: .init(parameters), selectedUsers: .init(selectedUsers), analytics: ServiceLocator.shared.analytics, - userIndicatorController: UserIndicatorControllerMock()) + userIndicatorController: UserIndicatorControllerMock(), + appSettings: ServiceLocator.shared.settings) }() static let emtpyViewModel = { @@ -184,7 +203,21 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview { createRoomParameters: .init(parameters), selectedUsers: .init([]), analytics: ServiceLocator.shared.analytics, - userIndicatorController: UserIndicatorControllerMock()) + userIndicatorController: UserIndicatorControllerMock(), + appSettings: ServiceLocator.shared.settings) + }() + + static let publicRoomViewModel = { + let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com")))) + let parameters = CreateRoomFlowParameters(isRoomPrivate: false) + let selectedUsers: [UserProfileProxy] = [.mockAlice, .mockBob, .mockCharlie] + ServiceLocator.shared.settings.knockingEnabled = true + return CreateRoomViewModel(userSession: userSession, + createRoomParameters: .init(parameters), + selectedUsers: .init([]), + analytics: ServiceLocator.shared.analytics, + userIndicatorController: UserIndicatorControllerMock(), + appSettings: ServiceLocator.shared.settings) }() static var previews: some View { @@ -196,5 +229,9 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview { CreateRoomScreen(context: emtpyViewModel.context) } .previewDisplayName("Create Room without users") + NavigationStack { + CreateRoomScreen(context: publicRoomViewModel.context) + } + .previewDisplayName("Create Public Room") } } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift index 4bff8d369..7e186b3a0 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift @@ -49,6 +49,7 @@ protocol DeveloperOptionsProtocol: AnyObject { var enableOnlySignedDeviceIsolationMode: Bool { get set } var elementCallBaseURLOverride: URL? { get set } var identityPinningViolationNotificationsEnabled: Bool { get set } + var knockingEnabled: Bool { get set } } extension AppSettings: DeveloperOptionsProtocol { } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift index df1afd540..42a3000fa 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -55,6 +55,13 @@ struct DeveloperOptionsScreen: View { } } + Section("Join rules") { + Toggle(isOn: $context.knockingEnabled) { + Text("Knocking") + Text("Experimental, still using mocked data") + } + } + Section { Toggle(isOn: $context.enableOnlySignedDeviceIsolationMode) { Text("Exclude insecure devices when sending/receiving messages") diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 8ed91d96c..32fdf374a 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -365,8 +365,9 @@ class ClientProxy: ClientProxyProtocol { } } - func createRoom(name: String, topic: String?, isRoomPrivate: Bool, userIDs: [String], avatarURL: URL?) async -> Result { + func createRoom(name: String, topic: String?, isRoomPrivate: Bool, isKnockingOnly: Bool, userIDs: [String], avatarURL: URL?) async -> Result { do { + // TODO: Revisit once the SDK supports the knocking API let parameters = CreateRoomParameters(name: name, topic: topic, isEncrypted: isRoomPrivate, diff --git a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift index a942ec638..0726ba83e 100644 --- a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift +++ b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift @@ -138,7 +138,7 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol { func createDirectRoom(with userID: String, expectedRoomName: String?) async -> Result - func createRoom(name: String, topic: String?, isRoomPrivate: Bool, userIDs: [String], avatarURL: URL?) async -> Result + func createRoom(name: String, topic: String?, isRoomPrivate: Bool, isKnockingOnly: Bool, userIDs: [String], avatarURL: URL?) async -> Result func joinRoom(_ roomID: String, via: [String]) async -> Result diff --git a/ElementX/Sources/Services/CreateRoom/CreateRoomFlowParameters.swift b/ElementX/Sources/Services/CreateRoom/CreateRoomFlowParameters.swift index d61a7e79e..d2e03d96d 100644 --- a/ElementX/Sources/Services/CreateRoom/CreateRoomFlowParameters.swift +++ b/ElementX/Sources/Services/CreateRoom/CreateRoomFlowParameters.swift @@ -12,5 +12,6 @@ struct CreateRoomFlowParameters { var name = "" var topic = "" var isRoomPrivate = true + var isKnockingOnly = false var avatarImageMedia: MediaInfo? } diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Public-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Public-Room.png new file mode 100644 index 000000000..97c0f2fdb --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-en-GB.Create-Public-Room.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2b6822fdd0ddc3b3c87d0142377a0951e9478c2fb1a1b840204589b2bc6270e +size 175831 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Public-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Public-Room.png new file mode 100644 index 000000000..4a25b1b65 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPad-pseudo.Create-Public-Room.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1b805b55944beb58caccd5dbf4e98261582fe006ce8c08014bfaf26c13eea75f +size 211876 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Public-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Public-Room.png new file mode 100644 index 000000000..2106fa84d --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-en-GB.Create-Public-Room.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0fccdc54a2d88e39353bdb49f32040749072dd17ac3c6cacc165ab6f0812d500 +size 123697 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Public-Room.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Public-Room.png new file mode 100644 index 000000000..712e0347a --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_createRoom-iPhone-16-pseudo.Create-Public-Room.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35ecf1e814eedd5f2d388224aee6fc885b1d9e1fe174b9199d9fe7bb9004a355 +size 164558 diff --git a/UnitTests/Sources/CreateRoomViewModelTests.swift b/UnitTests/Sources/CreateRoomViewModelTests.swift index 2dc1940e2..b145dfbfd 100644 --- a/UnitTests/Sources/CreateRoomViewModelTests.swift +++ b/UnitTests/Sources/CreateRoomViewModelTests.swift @@ -29,11 +29,13 @@ class CreateRoomScreenViewModelTests: XCTestCase { userSession = UserSessionMock(.init(clientProxy: clientProxy)) let parameters = CreateRoomFlowParameters() usersSubject.send([.mockAlice, .mockBob, .mockCharlie]) + ServiceLocator.shared.settings.knockingEnabled = true let viewModel = CreateRoomViewModel(userSession: userSession, createRoomParameters: .init(parameters), selectedUsers: usersSubject.asCurrentValuePublisher(), analytics: ServiceLocator.shared.analytics, - userIndicatorController: UserIndicatorControllerMock()) + userIndicatorController: UserIndicatorControllerMock(), + appSettings: ServiceLocator.shared.settings) self.viewModel = viewModel viewModel.actions.sink { [weak self] action in @@ -69,4 +71,38 @@ class CreateRoomScreenViewModelTests: XCTestCase { context.roomName = "A" XCTAssertTrue(context.viewState.canCreateRoom) } + + func testCreateKnockingRoom() async { + context.roomName = "A" + context.roomTopic = "B" + context.isRoomPrivate = false + context.isKnockingOnly = true + XCTAssertTrue(context.viewState.canCreateRoom) + + let expectation = expectation(description: "Wait for the room to be created") + clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLClosure = { _, _, isPrivate, isKnockingOnly, _, _ in + XCTAssertTrue(isKnockingOnly) + XCTAssertFalse(isPrivate) + defer { expectation.fulfill() } + return .success("") + } + context.send(viewAction: .createRoom) + await fulfillment(of: [expectation]) + } + + func testCreatePrivateRoomCantHaveKnockRule() async { + context.roomName = "A" + context.roomTopic = "B" + context.isRoomPrivate = true + context.isKnockingOnly = true + context.send(viewAction: .createRoom) + let expectation = expectation(description: "Wait for the room to be created") + clientProxy.createRoomNameTopicIsRoomPrivateIsKnockingOnlyUserIDsAvatarURLClosure = { _, _, isPrivate, isKnockingOnly, _, _ in + XCTAssertFalse(isKnockingOnly) + XCTAssertTrue(isPrivate) + expectation.fulfill() + return .success("") + } + await fulfillment(of: [expectation]) + } }