reused the room details view model for the space settings screen

This commit is contained in:
Mauro Romito
2025-10-31 14:23:08 +01:00
committed by Mauro
parent dc9ab56972
commit e59870b1e7
11 changed files with 103 additions and 222 deletions
@@ -11,13 +11,17 @@ import SwiftUI
struct SpaceSettingsScreenCoordinatorParameters {
let roomProxy: JoinedRoomProxyProtocol
let userSession: UserSessionProtocol
let analyticsService: AnalyticsService
let userIndicator: UserIndicatorControllerProtocol
let notificationSettingsProxy: NotificationSettingsProxyProtocol
let attributedStringBuilder: AttributedStringBuilderProtocol
let appSettings: AppSettings
}
enum SpaceSettingsScreenCoordinatorAction { }
final class SpaceSettingsScreenCoordinator: CoordinatorProtocol {
private let parameters: SpaceSettingsScreenCoordinatorParameters
private let viewModel: SpaceSettingsScreenViewModelProtocol
private let viewModel: RoomDetailsScreenViewModelProtocol
private var cancellables = Set<AnyCancellable>()
@@ -27,18 +31,32 @@ final class SpaceSettingsScreenCoordinator: CoordinatorProtocol {
}
init(parameters: SpaceSettingsScreenCoordinatorParameters) {
self.parameters = parameters
viewModel = SpaceSettingsScreenViewModel(roomProxy: parameters.roomProxy,
userSession: parameters.userSession)
viewModel = RoomDetailsScreenViewModel(roomProxy: parameters.roomProxy,
userSession: parameters.userSession,
analyticsService: parameters.analyticsService,
userIndicatorController: parameters.userIndicator,
notificationSettingsProxy: parameters.notificationSettingsProxy,
attributedStringBuilder: parameters.attributedStringBuilder,
appSettings: parameters.appSettings)
}
func start() {
viewModel.actionsPublisher.sink { [weak self] action in
viewModel.actions.sink { [weak self] action in
MXLog.info("Coordinator: received view model action: \(action)")
guard let self else { return }
switch action { }
switch action {
case .requestNotificationSettingsPresentation, .requestRecipientDetailsPresentation, .requestInvitePeoplePresentation, .leftRoom, .requestPollsHistoryPresentation, .requestRolesAndPermissionsPresentation, .startCall, .displayPinnedEventsTimeline, .displayMediaEventsTimeline, .displayKnockingRequests, .displayReportRoom:
break // Not handled in this context
case .requestEditDetailsPresentation:
break // TODO:
case .displaySecurityAndPrivacy:
break // TODO:
case .transferOwnership:
break // TODO:
case .requestMemberDetailsPresentation:
break // TODO:
}
}
.store(in: &cancellables)
}
@@ -1,28 +0,0 @@
//
// 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 SpaceSettingsScreenViewModelAction { }
struct SpaceSettingsScreenViewState: BindableState {
var details: RoomDetails
var joinedMembersCount: Int
var hasMemberIdentityVerificationStateViolations = false
var canEditBaseInfo = false
var canEditRolesOrPermissions = false
}
enum SpaceSettingsScreenViewAction {
case processTapEdit
case processTapSecurity
case processTapPeople
case processTapRolesAndPermissions
case processTapLeave
}
@@ -1,96 +0,0 @@
//
// 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 SpaceSettingsScreenViewModelType = StateStoreViewModelV2<SpaceSettingsScreenViewState, SpaceSettingsScreenViewAction>
class SpaceSettingsScreenViewModel: SpaceSettingsScreenViewModelType, SpaceSettingsScreenViewModelProtocol {
private let roomProxy: JoinedRoomProxyProtocol
private let userSession: UserSessionProtocol
private let actionsSubject: PassthroughSubject<SpaceSettingsScreenViewModelAction, Never> = .init()
var actionsPublisher: AnyPublisher<SpaceSettingsScreenViewModelAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
init(roomProxy: JoinedRoomProxyProtocol, userSession: UserSessionProtocol) {
self.roomProxy = roomProxy
self.userSession = userSession
super.init(initialViewState: .init(details: roomProxy.details,
joinedMembersCount: roomProxy.infoPublisher.value.joinedMembersCount),
mediaProvider: userSession.mediaProvider)
updateRoomInfo(roomProxy.infoPublisher.value)
setupRoomSubscription()
Task { await roomProxy.updateMembers() }
}
// MARK: - Public
override func process(viewAction: SpaceSettingsScreenViewAction) {
MXLog.info("View model: received view action: \(viewAction)")
switch viewAction {
case .processTapEdit:
break
case .processTapSecurity:
break
case .processTapPeople:
break
case .processTapRolesAndPermissions:
break
case .processTapLeave:
break
}
}
private func updateRoomInfo(_ roomInfo: RoomInfoProxyProtocol) {
state.joinedMembersCount = roomInfo.joinedMembersCount
state.details = roomProxy.details
if let powerLevels = roomInfo.powerLevels {
state.canEditRolesOrPermissions = powerLevels.canOwnUserEditRolesAndPermissions()
state.canEditBaseInfo = powerLevels.canOwnUserEditBaseInfo()
}
}
private func setupRoomSubscription() {
roomProxy.infoPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] roomInfo in
self?.updateRoomInfo(roomInfo)
}
.store(in: &cancellables)
roomProxy.membersPublisher.combineLatest(roomProxy.identityStatusChangesPublisher)
.sink { [weak self] _ in
Task { await self?.updateMemberIdentityVerificationStates() }
}
.store(in: &cancellables)
}
private func updateMemberIdentityVerificationStates() async {
guard roomProxy.infoPublisher.value.isEncrypted else {
// We don't care about identity statuses on non-encrypted rooms
return
}
for member in roomProxy.membersPublisher.value {
if case let .success(userIdentity) = await userSession.clientProxy.userIdentity(for: member.userID) {
if userIdentity?.verificationState == .verificationViolation {
state.hasMemberIdentityVerificationStateViolations = true
return
}
}
}
state.hasMemberIdentityVerificationStateViolations = false
}
}
@@ -1,14 +0,0 @@
//
// 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 SpaceSettingsScreenViewModelProtocol {
var actionsPublisher: AnyPublisher<SpaceSettingsScreenViewModelAction, Never> { get }
var context: SpaceSettingsScreenViewModelType.Context { get }
}
@@ -9,7 +9,7 @@ import Compound
import SwiftUI
struct SpaceSettingsScreen: View {
@Bindable var context: SpaceSettingsScreenViewModel.Context
@Bindable var context: RoomDetailsScreenViewModel.Context
var body: some View {
Form {
@@ -76,7 +76,7 @@ struct SpaceSettingsScreen: View {
Section {
ListRow(label: .default(title: L10n.screenSpaceSettingsSecurityAndPrivacy, icon: \.lock),
kind: .navigationLink {
context.send(viewAction: .processTapSecurity)
context.send(viewAction: .processTapSecurityAndPrivacy)
})
}
}
@@ -117,19 +117,29 @@ struct SpaceSettingsScreen: View {
// MARK: - Previews
struct SpaceSettingsScreen_Previews: PreviewProvider, TestablePreview {
static let ownerViewModel = SpaceSettingsScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Space",
avatarURL: .mockMXCAvatar,
isSpace: true,
canonicalAlias: "#space:matrix.org",
members: .allMembersAsCreator)),
userSession: UserSessionMock(.init()))
static let ownerViewModel = RoomDetailsScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Space",
avatarURL: .mockMXCAvatar,
isSpace: true,
canonicalAlias: "#space:matrix.org",
members: .allMembersAsCreator)),
userSession: UserSessionMock(.init()),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
appSettings: ServiceLocator.shared.settings)
static let userViewModel = SpaceSettingsScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Space",
avatarURL: .mockMXCAvatar,
isSpace: true,
canonicalAlias: "#space:matrix.org",
members: .allMembers)),
userSession: UserSessionMock(.init()))
static let userViewModel = RoomDetailsScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Space",
avatarURL: .mockMXCAvatar,
isSpace: true,
canonicalAlias: "#space:matrix.org",
members: .allMembers)),
userSession: UserSessionMock(.init()),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
appSettings: ServiceLocator.shared.settings)
static var previews: some View {
NavigationStack {