Create Room with knock rule (#3397)

* create knock room implementation (without SDK)

* Apply suggestions from code review

pr suggestions

Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com>

* pr suggestions

---------

Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com>
This commit is contained in:
Mauro
2024-10-14 11:52:38 +02:00
committed by GitHub
parent 25037a67ee
commit a16e1346fe
17 changed files with 149 additions and 37 deletions
@@ -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() {
@@ -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.
@@ -26,7 +26,8 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol
createRoomParameters: CurrentValuePublisher<CreateRoomFlowParameters, Never>,
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):
@@ -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")
}
}
@@ -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 { }
@@ -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")