implemented the navigation to the manage auth spaces screen

# Conflicts:
#	ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift
#	ElementX/Sources/FlowCoordinators/SpaceSettingsFlowCoordinator.swift
#	ElementX/Sources/Screens/SecurityAndPrivacyScreen/SecurityAndPrivacyScreenCoordinator.swift
#	ElementX/Sources/Screens/SecurityAndPrivacyScreen/SecurityAndPrivacyScreenModels.swift
This commit is contained in:
Mauro Romito
2025-11-27 17:36:34 +01:00
committed by Mauro
parent 2556eccc29
commit 0dcf1ffda2
15 changed files with 340 additions and 51 deletions
@@ -0,0 +1,53 @@
//
// Copyright 2025 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
// periphery:ignore:all - this is just a manageAuthorizedSpaces remove this comment once generating the final file
import Combine
import SwiftUI
struct ManageAuthorizedSpacesScreenCoordinatorParameters {
let authorizedSpacesSelection: AuthorizedSpacesSelection
let mediaProvider: MediaProviderProtocol
}
enum ManageAuthorizedSpacesScreenCoordinatorAction {
case dismiss
}
final class ManageAuthorizedSpacesScreenCoordinator: CoordinatorProtocol {
private let viewModel: ManageAuthorizedSpacesScreenViewModelProtocol
private var cancellables = Set<AnyCancellable>()
private let actionsSubject: PassthroughSubject<ManageAuthorizedSpacesScreenCoordinatorAction, Never> = .init()
var actionsPublisher: AnyPublisher<ManageAuthorizedSpacesScreenCoordinatorAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
init(parameters: ManageAuthorizedSpacesScreenCoordinatorParameters) {
viewModel = ManageAuthorizedSpacesScreenViewModel(authorizedSpacesSelection: parameters.authorizedSpacesSelection,
mediaProvider: parameters.mediaProvider)
}
func start() {
viewModel.actionsPublisher.sink { [weak self] action in
MXLog.info("Coordinator: received view model action: \(action)")
guard let self else { return }
switch action {
case .dismiss:
actionsSubject.send(.dismiss)
}
}
.store(in: &cancellables)
}
func toPresentable() -> AnyView {
AnyView(ManageAuthorizedSpacesScreen(context: viewModel.context))
}
}
@@ -0,0 +1,34 @@
//
// Copyright 2025 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import Foundation
enum ManageAuthorizedSpacesScreenViewModelAction {
case dismiss
}
struct ManageAuthorizedSpacesScreenViewState: BindableState {
let authorizedSpacesSelection: AuthorizedSpacesSelection
var desiredSelectedIDs: Set<String>
var hasChanges: Bool {
authorizedSpacesSelection.selectedIDs != desiredSelectedIDs
}
init(authorizedSpacesSelection: AuthorizedSpacesSelection) {
self.authorizedSpacesSelection = authorizedSpacesSelection
desiredSelectedIDs = authorizedSpacesSelection.selectedIDs
}
}
enum ManageAuthorizedSpacesScreenViewAction { }
struct AuthorizedSpacesSelection {
let joinedParentSpaces: [SpaceRoomProxyProtocol]
let unknownSpacesIDs: [String]
let selectedIDs: Set<String>
}
@@ -0,0 +1,30 @@
//
// Copyright 2025 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import Combine
import SwiftUI
typealias ManageAuthorizedSpacesScreenViewModelType = StateStoreViewModelV2<ManageAuthorizedSpacesScreenViewState, ManageAuthorizedSpacesScreenViewAction>
class ManageAuthorizedSpacesScreenViewModel: ManageAuthorizedSpacesScreenViewModelType, ManageAuthorizedSpacesScreenViewModelProtocol {
private let actionsSubject: PassthroughSubject<ManageAuthorizedSpacesScreenViewModelAction, Never> = .init()
var actionsPublisher: AnyPublisher<ManageAuthorizedSpacesScreenViewModelAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
init(authorizedSpacesSelection: AuthorizedSpacesSelection,
mediaProvider: MediaProviderProtocol) {
super.init(initialViewState: ManageAuthorizedSpacesScreenViewState(authorizedSpacesSelection: authorizedSpacesSelection),
mediaProvider: mediaProvider)
}
// MARK: - Public
override func process(viewAction: ManageAuthorizedSpacesScreenViewAction) {
MXLog.info("View model: received view action: \(viewAction)")
}
}
@@ -0,0 +1,14 @@
//
// Copyright 2025 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import Combine
@MainActor
protocol ManageAuthorizedSpacesScreenViewModelProtocol {
var actionsPublisher: AnyPublisher<ManageAuthorizedSpacesScreenViewModelAction, Never> { get }
var context: ManageAuthorizedSpacesScreenViewModelType.Context { get }
}
@@ -0,0 +1,36 @@
//
// Copyright 2025 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import Compound
import SwiftUI
struct ManageAuthorizedSpacesScreen: View {
@Bindable var context: ManageAuthorizedSpacesScreenViewModel.Context
var body: some View {
Form { }
.compoundList()
.navigationTitle("Manage spaces")
}
}
// MARK: - Previews
struct ManageAuthorizedSpacesScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = ManageAuthorizedSpacesScreenViewModel(authorizedSpacesSelection: .init(joinedParentSpaces: .mockJoinedSpaces,
unknownSpacesIDs: ["!unknown-space-id-1",
"!unknown-space-id-2",
"!unknown-space-id-3"],
selectedIDs: ["space1",
"space3",
"!unknown-space-id-2"]),
mediaProvider: MediaProviderMock(configuration: .init()))
static var previews: some View {
ManageAuthorizedSpacesScreen(context: viewModel.context)
}
}
@@ -19,6 +19,7 @@ struct SecurityAndPrivacyScreenCoordinatorParameters {
enum SecurityAndPrivacyScreenCoordinatorAction {
case displayEditAddressScreen
case dismiss
case displayManageAuthorizedSpacesScreen(AuthorizedSpacesSelection)
}
final class SecurityAndPrivacyScreenCoordinator: CoordinatorProtocol {
@@ -44,6 +45,8 @@ final class SecurityAndPrivacyScreenCoordinator: CoordinatorProtocol {
guard let self else { return }
switch action {
case .displayManageAuthorizedSpacesScreen(let selection):
actionsSubject.send(.displayManageAuthorizedSpacesScreen(selection))
case .displayEditAddressScreen:
actionsSubject.send(.displayEditAddressScreen)
case .dismiss:
@@ -11,6 +11,7 @@ import Foundation
enum SecurityAndPrivacyScreenViewModelAction {
case displayEditAddressScreen
case dismiss
case displayManageAuthorizedSpacesScreen(AuthorizedSpacesSelection)
}
struct SecurityAndPrivacyScreenViewState: BindableState {
@@ -81,8 +81,7 @@ class SecurityAndPrivacyScreenViewModel: SecurityAndPrivacyScreenViewModelType,
case .selectedSpaceMembersAccess:
handleSelectedSpaceMembersAccess()
case .manageSpaces:
// TODO: Implement multiple space selection
break
displayManageAuthorizedSpacesScreen()
}
}
@@ -236,15 +235,29 @@ 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
guard !state.bindings.desiredSettings.accessType.isSpaceUsers else {
return
}
switch state.spaceSelection {
case .singleJoined(let joinedParent):
state.bindings.desiredSettings.accessType = .spaceUsers(spaceIDs: [joinedParent.id])
case .singleUnknown(let id):
state.bindings.desiredSettings.accessType = .spaceUsers(spaceIDs: [id])
case .multiple:
displayManageAuthorizedSpacesScreen()
}
}
private func displayManageAuthorizedSpacesScreen() {
let joinedParentSpaces = state.joinedParentSpaces
let unknownSpaceIDs = state.currentSettings.accessType.spaceIDs.filter { id in
!joinedParentSpaces.contains(where: { $0.id == id })
}
let selectedIDs = Set(state.bindings.desiredSettings.accessType.spaceIDs)
actionsSubject.send(.displayManageAuthorizedSpacesScreen(.init(joinedParentSpaces: joinedParentSpaces,
unknownSpacesIDs: unknownSpaceIDs,
selectedIDs: selectedIDs)))
}
private static let loadingIndicatorIdentifier = "\(EditRoomAddressScreenViewModel.self)-Loading"