Allow joined rooms to be pushed within a space. (#4460)

* Allow joined rooms to be pushed within a space.

* Push a room in the space flow tests.

Also fixes some snapshots stale snapshots.

* Show the selected space/room within a space and set a custom title view.
This commit is contained in:
Doug
2025-09-03 17:48:49 +01:00
committed by GitHub
parent 927aea4bfe
commit 53eb1c1718
28 changed files with 170 additions and 44 deletions
@@ -13,12 +13,14 @@ import SwiftUI
struct SpaceScreenCoordinatorParameters {
let spaceRoomListProxy: SpaceRoomListProxyProtocol
let spaceServiceProxy: SpaceServiceProxyProtocol
let selectedSpaceRoomPublisher: CurrentValuePublisher<String?, Never>
let mediaProvider: MediaProviderProtocol
let userIndicatorController: UserIndicatorControllerProtocol
}
enum SpaceScreenCoordinatorAction {
case selectSpace(SpaceRoomListProxyProtocol)
case selectRoom(roomID: String)
}
final class SpaceScreenCoordinator: CoordinatorProtocol {
@@ -37,6 +39,7 @@ final class SpaceScreenCoordinator: CoordinatorProtocol {
viewModel = SpaceScreenViewModel(spaceRoomListProxy: parameters.spaceRoomListProxy,
spaceServiceProxy: parameters.spaceServiceProxy,
selectedSpaceRoomPublisher: parameters.selectedSpaceRoomPublisher,
mediaProvider: parameters.mediaProvider,
userIndicatorController: parameters.userIndicatorController)
}
@@ -49,6 +52,8 @@ final class SpaceScreenCoordinator: CoordinatorProtocol {
switch action {
case .selectSpace(let spaceRoomListProxy):
actionsSubject.send(.selectSpace(spaceRoomListProxy))
case .selectRoom(let roomID):
actionsSubject.send(.selectRoom(roomID: roomID))
}
}
.store(in: &cancellables)
@@ -9,6 +9,7 @@ import Foundation
enum SpaceScreenViewModelAction {
case selectSpace(SpaceRoomListProxyProtocol)
case selectRoom(roomID: String)
}
struct SpaceScreenViewState: BindableState {
@@ -16,8 +17,11 @@ struct SpaceScreenViewState: BindableState {
var isPaginating = false
var rooms: [SpaceRoomProxyProtocol]
var selectedSpaceRoomID: String?
var bindings = SpaceScreenViewStateBindings()
var spaceName: String { space.name ?? L10n.commonSpace }
}
struct SpaceScreenViewStateBindings { }
@@ -21,13 +21,15 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
init(spaceRoomListProxy: SpaceRoomListProxyProtocol,
spaceServiceProxy: SpaceServiceProxyProtocol,
selectedSpaceRoomPublisher: CurrentValuePublisher<String?, Never>,
mediaProvider: MediaProviderProtocol,
userIndicatorController: UserIndicatorControllerProtocol) {
self.spaceServiceProxy = spaceServiceProxy
self.userIndicatorController = userIndicatorController
super.init(initialViewState: SpaceScreenViewState(space: spaceRoomListProxy.spaceRoomProxy,
rooms: spaceRoomListProxy.spaceRoomsPublisher.value),
rooms: spaceRoomListProxy.spaceRoomsPublisher.value,
selectedSpaceRoomID: selectedSpaceRoomPublisher.value),
mediaProvider: mediaProvider)
spaceRoomListProxy.spaceRoomsPublisher
@@ -50,6 +52,10 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
}
}
.store(in: &cancellables)
selectedSpaceRoomPublisher
.weakAssign(to: \.state.selectedSpaceRoomID, on: self)
.store(in: &cancellables)
}
// MARK: - Public
@@ -61,8 +67,10 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
case .spaceAction(.select(let spaceRoomProxy)):
if spaceRoomProxy.isSpace {
Task { await selectSpace(spaceRoomProxy) }
} else {
#warning("Implement room flow")
} else if spaceRoomProxy.state == .joined {
// This probably doesn't need the state condition as the room flow will show a join screen,
// but we can allow this later, once we've updated the design to indicate the parent space.
actionsSubject.send(.selectRoom(roomID: spaceRoomProxy.id))
}
case .spaceAction(.join(let spaceID)):
#warning("Implement joining.")
@@ -19,16 +19,17 @@ struct SpaceScreen: View {
rooms
}
}
.navigationTitle(context.viewState.space.name ?? L10n.commonSpace)
.navigationBarTitleDisplayMode(.inline)
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
.navigationTitle(context.viewState.spaceName)
.navigationBarTitleDisplayMode(.inline)
.toolbar { toolbar }
}
@ViewBuilder
var rooms: some View {
ForEach(context.viewState.rooms, id: \.id) { spaceRoomProxy in
SpaceRoomCell(spaceRoomProxy: spaceRoomProxy,
isSelected: false,
isSelected: spaceRoomProxy.id == context.viewState.selectedSpaceRoomID,
mediaProvider: context.mediaProvider) { action in
context.send(viewAction: .spaceAction(action))
}
@@ -39,6 +40,16 @@ struct SpaceScreen: View {
.padding()
}
}
var toolbar: some ToolbarContent {
// Use the same trick as the RoomScreen for a leading title view that
// also hides the navigation title.
ToolbarItem(placement: .principal) {
RoomHeaderView(roomName: context.viewState.spaceName,
roomAvatar: context.viewState.space.avatar,
mediaProvider: context.mediaProvider)
}
}
}
// MARK: - Previews
@@ -66,6 +77,7 @@ struct SpaceScreen_Previews: PreviewProvider, TestablePreview {
let viewModel = SpaceScreenViewModel(spaceRoomListProxy: spaceRoomListProxy,
spaceServiceProxy: SpaceServiceProxyMock(.init()),
selectedSpaceRoomPublisher: .init(nil),
mediaProvider: MediaProviderMock(configuration: .init()),
userIndicatorController: UserIndicatorControllerMock())
return viewModel