Refactor SpaceServiceProxy.joinedSpaces to topLevelSpaces.

This commit is contained in:
Doug
2026-01-05 15:35:11 +00:00
committed by Doug
parent 5d57b21222
commit 9304c3cc65
12 changed files with 39 additions and 39 deletions

View File

@@ -287,7 +287,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
} }
.store(in: &cancellables) .store(in: &cancellables)
userSession.clientProxy.spaceService.joinedSpacesPublisher userSession.clientProxy.spaceService.topLevelSpacesPublisher
.map { $0.isEmpty ? .hidden : nil } .map { $0.isEmpty ? .hidden : nil }
.weakAssign(to: \.chatsTabDetails.barVisibilityOverride, on: self) .weakAssign(to: \.chatsTabDetails.barVisibilityOverride, on: self)
.store(in: &cancellables) .store(in: &cancellables)

View File

@@ -121,7 +121,7 @@ extension ClientProxyMock {
roomProxy.loadOrFetchEventDetailsForReturnValue = .success(TimelineEventSDKMock()) roomProxy.loadOrFetchEventDetailsForReturnValue = .success(TimelineEventSDKMock())
return .joined(roomProxy) return .joined(roomProxy)
} }
} else if let spaceRoomProxy = configuration.spaceServiceConfiguration.joinedSpaces.first(where: { $0.id == identifier }) { } else if let spaceRoomProxy = configuration.spaceServiceConfiguration.topLevelSpaces.first(where: { $0.id == identifier }) {
let roomProxy = await JoinedRoomProxyMock(.init(id: spaceRoomProxy.id, name: spaceRoomProxy.name, isSpace: spaceRoomProxy.isSpace)) let roomProxy = await JoinedRoomProxyMock(.init(id: spaceRoomProxy.id, name: spaceRoomProxy.name, isSpace: spaceRoomProxy.isSpace))
roomProxy.loadOrFetchEventDetailsForReturnValue = .success(TimelineEventSDKMock()) roomProxy.loadOrFetchEventDetailsForReturnValue = .success(TimelineEventSDKMock())
return .joined(roomProxy) return .joined(roomProxy)

View File

@@ -16685,11 +16685,11 @@ class SpaceRoomProxyMock: SpaceRoomProxyProtocol, @unchecked Sendable {
} }
class SpaceServiceProxyMock: SpaceServiceProxyProtocol, @unchecked Sendable { class SpaceServiceProxyMock: SpaceServiceProxyProtocol, @unchecked Sendable {
var joinedSpacesPublisher: CurrentValuePublisher<[SpaceRoomProxyProtocol], Never> { var topLevelSpacesPublisher: CurrentValuePublisher<[SpaceRoomProxyProtocol], Never> {
get { return underlyingJoinedSpacesPublisher } get { return underlyingTopLevelSpacesPublisher }
set(value) { underlyingJoinedSpacesPublisher = value } set(value) { underlyingTopLevelSpacesPublisher = value }
} }
var underlyingJoinedSpacesPublisher: CurrentValuePublisher<[SpaceRoomProxyProtocol], Never>! var underlyingTopLevelSpacesPublisher: CurrentValuePublisher<[SpaceRoomProxyProtocol], Never>!
//MARK: - spaceRoomList //MARK: - spaceRoomList

View File

@@ -12,7 +12,7 @@ import MatrixRustSDK
extension SpaceServiceProxyMock { extension SpaceServiceProxyMock {
struct Configuration { struct Configuration {
var joinedSpaces: [SpaceRoomProxyProtocol] = [] var topLevelSpaces: [SpaceRoomProxyProtocol] = []
var joinedParentSpaces: [SpaceRoomProxyProtocol] = [] var joinedParentSpaces: [SpaceRoomProxyProtocol] = []
var spaceRoomLists: [String: SpaceRoomListProxyMock] = [:] var spaceRoomLists: [String: SpaceRoomListProxyMock] = [:]
var leaveSpaceRooms: [LeaveSpaceRoom] = [] var leaveSpaceRooms: [LeaveSpaceRoom] = []
@@ -21,7 +21,7 @@ extension SpaceServiceProxyMock {
convenience init(_ configuration: Configuration) { convenience init(_ configuration: Configuration) {
self.init() self.init()
joinedSpacesPublisher = .init(configuration.joinedSpaces) topLevelSpacesPublisher = .init(configuration.topLevelSpaces)
joinedParentsChildIDReturnValue = .success(configuration.joinedParentSpaces) joinedParentsChildIDReturnValue = .success(configuration.joinedParentSpaces)
spaceRoomListSpaceIDClosure = { spaceID in spaceRoomListSpaceIDClosure = { spaceID in
if let spaceRoomList = configuration.spaceRoomLists[spaceID] { if let spaceRoomList = configuration.spaceRoomLists[spaceID] {
@@ -35,7 +35,7 @@ extension SpaceServiceProxyMock {
leaveHandle: LeaveSpaceHandleSDKMock(.init(rooms: configuration.leaveSpaceRooms)))) leaveHandle: LeaveSpaceHandleSDKMock(.init(rooms: configuration.leaveSpaceRooms))))
} }
spaceForIdentifierSpaceIDClosure = { spaceID in spaceForIdentifierSpaceIDClosure = { spaceID in
.success(configuration.joinedSpaces.first { $0.id == spaceID }) .success(configuration.topLevelSpaces.first { $0.id == spaceID })
} }
} }
} }
@@ -49,6 +49,6 @@ extension SpaceServiceProxyMock.Configuration {
($0.id, SpaceRoomListProxyMock(.init(spaceRoomProxy: $0, initialSpaceRooms: .mockSingleRoom))) ($0.id, SpaceRoomListProxyMock(.init(spaceRoomProxy: $0, initialSpaceRooms: .mockSingleRoom)))
} }
return .init(joinedSpaces: .mockJoinedSpaces, spaceRoomLists: .init(uniqueKeysWithValues: spaceRoomLists + subSpaceRoomLists)) return .init(topLevelSpaces: .mockJoinedSpaces, spaceRoomLists: .init(uniqueKeysWithValues: spaceRoomLists + subSpaceRoomLists))
} }
} }

View File

@@ -18,7 +18,7 @@ struct SpaceListScreenViewState: BindableState {
var userDisplayName: String? var userDisplayName: String?
var userAvatarURL: URL? var userAvatarURL: URL?
var joinedSpaces: [SpaceRoomProxyProtocol] var topLevelSpaces: [SpaceRoomProxyProtocol]
var selectedSpaceID: String? var selectedSpaceID: String?
var bindings: SpaceListScreenViewStateBindings var bindings: SpaceListScreenViewStateBindings

View File

@@ -30,13 +30,13 @@ class SpaceListScreenViewModel: SpaceListScreenViewModelType, SpaceListScreenVie
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
super.init(initialViewState: SpaceListScreenViewState(userID: userSession.clientProxy.userID, super.init(initialViewState: SpaceListScreenViewState(userID: userSession.clientProxy.userID,
joinedSpaces: spaceServiceProxy.joinedSpacesPublisher.value, topLevelSpaces: spaceServiceProxy.topLevelSpacesPublisher.value,
bindings: .init()), bindings: .init()),
mediaProvider: userSession.mediaProvider) mediaProvider: userSession.mediaProvider)
spaceServiceProxy.joinedSpacesPublisher spaceServiceProxy.topLevelSpacesPublisher
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.weakAssign(to: \.state.joinedSpaces, on: self) .weakAssign(to: \.state.topLevelSpaces, on: self)
.store(in: &cancellables) .store(in: &cancellables)
selectedSpacePublisher selectedSpacePublisher

View File

@@ -40,7 +40,7 @@ struct SpaceListScreen: View {
.foregroundStyle(.compound.textPrimary) .foregroundStyle(.compound.textPrimary)
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
Text(L10n.commonSpaces(context.viewState.joinedSpaces.count)) Text(L10n.commonSpaces(context.viewState.topLevelSpaces.count))
.font(.compound.bodyLG) .font(.compound.bodyLG)
.foregroundStyle(.compound.textSecondary) .foregroundStyle(.compound.textSecondary)
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
@@ -63,7 +63,7 @@ struct SpaceListScreen: View {
} }
var spaces: some View { var spaces: some View {
ForEach(context.viewState.joinedSpaces, id: \.id) { spaceRoomProxy in ForEach(context.viewState.topLevelSpaces, id: \.id) { spaceRoomProxy in
SpaceRoomCell(spaceRoomProxy: spaceRoomProxy, SpaceRoomCell(spaceRoomProxy: spaceRoomProxy,
isSelected: spaceRoomProxy.id == context.viewState.selectedSpaceID, isSelected: spaceRoomProxy.id == context.viewState.selectedSpaceID,
mediaProvider: context.mediaProvider) { action in mediaProvider: context.mediaProvider) { action in
@@ -111,7 +111,7 @@ struct SpaceListScreen_Previews: PreviewProvider, TestablePreview {
static func makeViewModel() -> SpaceListScreenViewModel { static func makeViewModel() -> SpaceListScreenViewModel {
let clientProxy = ClientProxyMock(.init()) let clientProxy = ClientProxyMock(.init())
clientProxy.spaceService = SpaceServiceProxyMock(.init(joinedSpaces: .mockJoinedSpaces)) clientProxy.spaceService = SpaceServiceProxyMock(.init(topLevelSpaces: .mockJoinedSpaces))
let viewModel = SpaceListScreenViewModel(userSession: UserSessionMock(.init(clientProxy: clientProxy)), let viewModel = SpaceListScreenViewModel(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
selectedSpacePublisher: .init(nil), selectedSpacePublisher: .init(nil),

View File

@@ -13,9 +13,9 @@ import MatrixRustSDK
class SpaceServiceProxy: SpaceServiceProxyProtocol { class SpaceServiceProxy: SpaceServiceProxyProtocol {
private let spaceService: SpaceServiceProtocol private let spaceService: SpaceServiceProtocol
private var joinedSpacesHandle: TaskHandle? private var topLevelSpacesHandle: TaskHandle?
private let spacesSubject = CurrentValueSubject<[SpaceRoomProxyProtocol], Never>([]) private let spacesSubject = CurrentValueSubject<[SpaceRoomProxyProtocol], Never>([])
var joinedSpacesPublisher: CurrentValuePublisher<[SpaceRoomProxyProtocol], Never> { var topLevelSpacesPublisher: CurrentValuePublisher<[SpaceRoomProxyProtocol], Never> {
spacesSubject.asCurrentValuePublisher() spacesSubject.asCurrentValuePublisher()
} }
@@ -26,7 +26,7 @@ class SpaceServiceProxy: SpaceServiceProxyProtocol {
} }
private func setupSubscriptions() async { private func setupSubscriptions() async {
joinedSpacesHandle = await spaceService.subscribeToTopLevelJoinedSpaces(listener: SDKListener { [weak self] updates in topLevelSpacesHandle = await spaceService.subscribeToTopLevelJoinedSpaces(listener: SDKListener { [weak self] updates in
self?.handleUpdates(updates) self?.handleUpdates(updates)
}) })
} }

View File

@@ -15,7 +15,7 @@ enum SpaceServiceProxyError: Error {
// sourcery: AutoMockable // sourcery: AutoMockable
protocol SpaceServiceProxyProtocol { protocol SpaceServiceProxyProtocol {
var joinedSpacesPublisher: CurrentValuePublisher<[SpaceRoomProxyProtocol], Never> { get } var topLevelSpacesPublisher: CurrentValuePublisher<[SpaceRoomProxyProtocol], Never> { get }
func spaceRoomList(spaceID: String) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError> func spaceRoomList(spaceID: String) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>
/// Returns a joined space given its identifier /// Returns a joined space given its identifier

View File

@@ -596,7 +596,7 @@ class MockScreen: Identifiable {
let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com", let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com",
deviceID: "MOCKCLIENT", deviceID: "MOCKCLIENT",
roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(roomSummaries))), roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(roomSummaries))),
spaceServiceConfiguration: .init(joinedSpaces: .mockSingleRoom), spaceServiceConfiguration: .init(topLevelSpaces: .mockSingleRoom),
roomPreviews: [SpaceRoomProxyProtocol].mockSpaceList.map(RoomPreviewProxyMock.init))) roomPreviews: [SpaceRoomProxyProtocol].mockSpaceList.map(RoomPreviewProxyMock.init)))
// The tab bar remains hidden for the non-spaces tests as we don't supply any mock spaces. // The tab bar remains hidden for the non-spaces tests as we don't supply any mock spaces.

View File

@@ -261,7 +261,7 @@ class SecurityAndPrivacyScreenViewModelTests: XCTestCase {
let space = singleRoom[0] let space = singleRoom[0]
let allSpaces = joinedParentSpaces + singleRoom let allSpaces = joinedParentSpaces + singleRoom
setupViewModel(joinedParentSpaces: joinedParentSpaces, setupViewModel(joinedParentSpaces: joinedParentSpaces,
joinedSpaces: allSpaces, topLevelSpaces: allSpaces,
joinRule: .restricted(rules: [.roomMembership(roomId: space.id), joinRule: .restricted(rules: [.roomMembership(roomId: space.id),
.roomMembership(roomId: "unknownSpaceID")])) .roomMembership(roomId: "unknownSpaceID")]))
@@ -425,7 +425,7 @@ class SecurityAndPrivacyScreenViewModelTests: XCTestCase {
// MARK: - Helpers // MARK: - Helpers
private func setupViewModel(joinedParentSpaces: [SpaceRoomProxyProtocol], private func setupViewModel(joinedParentSpaces: [SpaceRoomProxyProtocol],
joinedSpaces: [SpaceRoomProxyProtocol] = [], topLevelSpaces: [SpaceRoomProxyProtocol] = [],
joinRule: JoinRule) { joinRule: JoinRule) {
let appSettings = AppSettings() let appSettings = AppSettings()
appSettings.spaceSettingsEnabled = true appSettings.spaceSettingsEnabled = true
@@ -440,7 +440,7 @@ class SecurityAndPrivacyScreenViewModelTests: XCTestCase {
viewModel = SecurityAndPrivacyScreenViewModel(roomProxy: roomProxy, viewModel = SecurityAndPrivacyScreenViewModel(roomProxy: roomProxy,
clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org", clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org",
spaceServiceConfiguration: .init(joinedSpaces: joinedSpaces, spaceServiceConfiguration: .init(topLevelSpaces: topLevelSpaces,
joinedParentSpaces: joinedParentSpaces))), joinedParentSpaces: joinedParentSpaces))),
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: appSettings) appSettings: appSettings)

View File

@@ -13,7 +13,7 @@ import XCTest
@MainActor @MainActor
class SpaceListScreenViewModelTests: XCTestCase { class SpaceListScreenViewModelTests: XCTestCase {
var joinedSpacesSubject: CurrentValueSubject<[SpaceRoomProxyProtocol], Never>! var topLevelSpacesSubject: CurrentValueSubject<[SpaceRoomProxyProtocol], Never>!
var spaceServiceProxy: SpaceServiceProxyMock! var spaceServiceProxy: SpaceServiceProxyMock!
var appSettings: AppSettings! var appSettings: AppSettings!
@@ -34,29 +34,29 @@ class SpaceListScreenViewModelTests: XCTestCase {
func testInitialState() { func testInitialState() {
setupViewModel() setupViewModel()
XCTAssertEqual(context.viewState.joinedSpaces.count, 3) XCTAssertEqual(context.viewState.topLevelSpaces.count, 3)
} }
func testJoinedSpacesSubscription() async throws { func testTopLevelSpacesSubscription() async throws {
setupViewModel() setupViewModel()
var deferred = deferFulfillment(context.observe(\.viewState.joinedSpaces)) { $0.count == 0 } var deferred = deferFulfillment(context.observe(\.viewState.topLevelSpaces)) { $0.count == 0 }
joinedSpacesSubject.send([]) topLevelSpacesSubject.send([])
try await deferred.fulfill() try await deferred.fulfill()
XCTAssertEqual(context.viewState.joinedSpaces.count, 0) XCTAssertEqual(context.viewState.topLevelSpaces.count, 0)
deferred = deferFulfillment(context.observe(\.viewState.joinedSpaces)) { $0.count == 1 } deferred = deferFulfillment(context.observe(\.viewState.topLevelSpaces)) { $0.count == 1 }
joinedSpacesSubject.send([ topLevelSpacesSubject.send([
SpaceRoomProxyMock(.init(isSpace: true)) SpaceRoomProxyMock(.init(isSpace: true))
]) ])
try await deferred.fulfill() try await deferred.fulfill()
XCTAssertEqual(context.viewState.joinedSpaces.count, 1) XCTAssertEqual(context.viewState.topLevelSpaces.count, 1)
} }
func testSelectingSpace() async throws { func testSelectingSpace() async throws {
setupViewModel() setupViewModel()
let selectedSpace = joinedSpacesSubject.value[0] let selectedSpace = topLevelSpacesSubject.value[0]
let deferred = deferFulfillment(viewModel.actionsPublisher) { _ in true } let deferred = deferFulfillment(viewModel.actionsPublisher) { _ in true }
viewModel.context.send(viewAction: .spaceAction(.select(selectedSpace))) viewModel.context.send(viewAction: .spaceAction(.select(selectedSpace)))
let action = try await deferred.fulfill() let action = try await deferred.fulfill()
@@ -96,15 +96,15 @@ class SpaceListScreenViewModelTests: XCTestCase {
let clientProxy = ClientProxyMock(.init()) let clientProxy = ClientProxyMock(.init())
let userSession = UserSessionMock(.init(clientProxy: clientProxy)) let userSession = UserSessionMock(.init(clientProxy: clientProxy))
joinedSpacesSubject = .init([ topLevelSpacesSubject = .init([
SpaceRoomProxyMock(.init(id: "space1", isSpace: true)), SpaceRoomProxyMock(.init(id: "space1", isSpace: true)),
SpaceRoomProxyMock(.init(id: "space2", isSpace: true)), SpaceRoomProxyMock(.init(id: "space2", isSpace: true)),
SpaceRoomProxyMock(.init(id: "space3", isSpace: true)) SpaceRoomProxyMock(.init(id: "space3", isSpace: true))
]) ])
spaceServiceProxy = SpaceServiceProxyMock(.init()) spaceServiceProxy = SpaceServiceProxyMock(.init())
spaceServiceProxy.joinedSpacesPublisher = joinedSpacesSubject.asCurrentValuePublisher() spaceServiceProxy.topLevelSpacesPublisher = topLevelSpacesSubject.asCurrentValuePublisher()
spaceServiceProxy.spaceRoomListSpaceIDClosure = { [joinedSpacesSubject] spaceID in spaceServiceProxy.spaceRoomListSpaceIDClosure = { [topLevelSpacesSubject] spaceID in
guard let spaceRoomProxy = joinedSpacesSubject?.value.first(where: { $0.id == spaceID }) else { return .failure(.missingSpace) } guard let spaceRoomProxy = topLevelSpacesSubject?.value.first(where: { $0.id == spaceID }) else { return .failure(.missingSpace) }
return .success(SpaceRoomListProxyMock(.init(spaceRoomProxy: spaceRoomProxy))) return .success(SpaceRoomListProxyMock(.init(spaceRoomProxy: spaceRoomProxy)))
} }
clientProxy.spaceService = spaceServiceProxy clientProxy.spaceService = spaceServiceProxy