Add some new space properties. (#4544)

* Use the via property on SpaceRoom.

* Fall back to the canonical alias as a space room name if needs be.

* Use SpaceRoom.isDirect for computing the name/avatar.

* Pass in the parent space as a workaround for no restricted join rules.
This commit is contained in:
Doug
2025-09-26 10:03:11 +01:00
committed by GitHub
parent 3d5901f90d
commit 12dc6f42ba
30 changed files with 178 additions and 102 deletions
+4
View File
@@ -2880,6 +2880,10 @@ internal enum L10n {
internal static func screenSpaceListDetails(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "screen_space_list_details", String(describing: p1), String(describing: p2))
}
/// %1$@ space
internal static func screenSpaceListParentSpace(_ p1: Any) -> String {
return L10n.tr("Localizable", "screen_space_list_parent_space", String(describing: p1))
}
/// Spaces
internal static var screenSpaceListTitle: String { return L10n.tr("Localizable", "screen_space_list_title") }
/// An error occurred when trying to start a chat
@@ -15938,6 +15938,8 @@ class SpaceRoomProxyMock: SpaceRoomProxyProtocol, @unchecked Sendable {
set(value) { underlyingIsSpace = value }
}
var underlyingIsSpace: Bool!
var isDirect: Bool?
var parent: SpaceRoomProxyProtocol?
var childrenCount: Int {
get { return underlyingChildrenCount }
set(value) { underlyingChildrenCount = value }
@@ -15959,6 +15961,7 @@ class SpaceRoomProxyMock: SpaceRoomProxyProtocol, @unchecked Sendable {
}
var underlyingGuestCanJoin: Bool!
var state: Membership?
var via: [String] = []
}
class SpaceServiceProxyMock: SpaceServiceProxyProtocol, @unchecked Sendable {
@@ -15970,15 +15973,15 @@ class SpaceServiceProxyMock: SpaceServiceProxyProtocol, @unchecked Sendable {
//MARK: - spaceRoomList
var spaceRoomListSpaceIDUnderlyingCallsCount = 0
var spaceRoomListSpaceIDCallsCount: Int {
var spaceRoomListSpaceIDParentUnderlyingCallsCount = 0
var spaceRoomListSpaceIDParentCallsCount: Int {
get {
if Thread.isMainThread {
return spaceRoomListSpaceIDUnderlyingCallsCount
return spaceRoomListSpaceIDParentUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = spaceRoomListSpaceIDUnderlyingCallsCount
returnValue = spaceRoomListSpaceIDParentUnderlyingCallsCount
}
return returnValue!
@@ -15986,29 +15989,29 @@ class SpaceServiceProxyMock: SpaceServiceProxyProtocol, @unchecked Sendable {
}
set {
if Thread.isMainThread {
spaceRoomListSpaceIDUnderlyingCallsCount = newValue
spaceRoomListSpaceIDParentUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
spaceRoomListSpaceIDUnderlyingCallsCount = newValue
spaceRoomListSpaceIDParentUnderlyingCallsCount = newValue
}
}
}
}
var spaceRoomListSpaceIDCalled: Bool {
return spaceRoomListSpaceIDCallsCount > 0
var spaceRoomListSpaceIDParentCalled: Bool {
return spaceRoomListSpaceIDParentCallsCount > 0
}
var spaceRoomListSpaceIDReceivedSpaceID: String?
var spaceRoomListSpaceIDReceivedInvocations: [String] = []
var spaceRoomListSpaceIDParentReceivedArguments: (spaceID: String, parent: SpaceRoomProxyProtocol?)?
var spaceRoomListSpaceIDParentReceivedInvocations: [(spaceID: String, parent: SpaceRoomProxyProtocol?)] = []
var spaceRoomListSpaceIDUnderlyingReturnValue: Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>!
var spaceRoomListSpaceIDReturnValue: Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>! {
var spaceRoomListSpaceIDParentUnderlyingReturnValue: Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>!
var spaceRoomListSpaceIDParentReturnValue: Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>! {
get {
if Thread.isMainThread {
return spaceRoomListSpaceIDUnderlyingReturnValue
return spaceRoomListSpaceIDParentUnderlyingReturnValue
} else {
var returnValue: Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>? = nil
DispatchQueue.main.sync {
returnValue = spaceRoomListSpaceIDUnderlyingReturnValue
returnValue = spaceRoomListSpaceIDParentUnderlyingReturnValue
}
return returnValue!
@@ -16016,26 +16019,26 @@ class SpaceServiceProxyMock: SpaceServiceProxyProtocol, @unchecked Sendable {
}
set {
if Thread.isMainThread {
spaceRoomListSpaceIDUnderlyingReturnValue = newValue
spaceRoomListSpaceIDParentUnderlyingReturnValue = newValue
} else {
DispatchQueue.main.sync {
spaceRoomListSpaceIDUnderlyingReturnValue = newValue
spaceRoomListSpaceIDParentUnderlyingReturnValue = newValue
}
}
}
}
var spaceRoomListSpaceIDClosure: ((String) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>)?
var spaceRoomListSpaceIDParentClosure: ((String, SpaceRoomProxyProtocol?) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>)?
func spaceRoomList(spaceID: String) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError> {
spaceRoomListSpaceIDCallsCount += 1
spaceRoomListSpaceIDReceivedSpaceID = spaceID
func spaceRoomList(spaceID: String, parent: SpaceRoomProxyProtocol?) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError> {
spaceRoomListSpaceIDParentCallsCount += 1
spaceRoomListSpaceIDParentReceivedArguments = (spaceID: spaceID, parent: parent)
DispatchQueue.main.async {
self.spaceRoomListSpaceIDReceivedInvocations.append(spaceID)
self.spaceRoomListSpaceIDParentReceivedInvocations.append((spaceID: spaceID, parent: parent))
}
if let spaceRoomListSpaceIDClosure = spaceRoomListSpaceIDClosure {
return await spaceRoomListSpaceIDClosure(spaceID)
if let spaceRoomListSpaceIDParentClosure = spaceRoomListSpaceIDParentClosure {
return await spaceRoomListSpaceIDParentClosure(spaceID, parent)
} else {
return spaceRoomListSpaceIDReturnValue
return spaceRoomListSpaceIDParentReturnValue
}
}
}
@@ -15,6 +15,8 @@ extension SpaceRoomProxyMock {
var avatarURL: URL?
var isSpace: Bool
var isDirect: Bool?
var parent: SpaceRoomProxyProtocol?
var childrenCount = 0
var joinedMembersCount = 0
@@ -35,6 +37,8 @@ extension SpaceRoomProxyMock {
name = configuration.name
avatarURL = configuration.avatarURL
isSpace = configuration.isSpace
isDirect = configuration.isDirect
parent = configuration.parent
childrenCount = configuration.childrenCount
joinedMembersCount = configuration.joinedMembersCount
heroes = configuration.heroes
@@ -125,8 +129,10 @@ extension [SpaceRoomProxyProtocol] {
SpaceRoomProxyMock(.init(id: "!\(typeName.lowercased())3:matrix.org",
name: "Joined \(typeName)",
isSpace: isSpace,
parent: SpaceRoomProxyMock(.init(name: "Company", isSpace: true)),
joinedMembersCount: 123,
topic: "Discussion on specific topic goes here.",
joinRule: .restricted(rules: []),
state: .joined))
]
}
@@ -19,7 +19,7 @@ extension SpaceServiceProxyMock {
self.init()
joinedSpacesPublisher = .init(configuration.joinedSpaces)
spaceRoomListSpaceIDClosure = { spaceID in
spaceRoomListSpaceIDParentClosure = { spaceID, _ in
if let spaceRoomList = configuration.spaceRoomLists[spaceID] {
.success(spaceRoomList)
} else {
@@ -286,7 +286,7 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo
return
}
switch await clientProxy.spaceService.spaceRoomList(spaceID: roomID) {
switch await clientProxy.spaceService.spaceRoomList(spaceID: roomID, parent: nil) {
case .success(let spaceRoomListProxy):
actionsSubject.send(.joined(.space(spaceRoomListProxy)))
case .failure(let error):
@@ -14,8 +14,6 @@ struct SpaceHeaderView: View {
@State private var isPresentingTopic = false
var title: String { spaceRoomProxy.name ?? "" }
var body: some View {
VStack(spacing: 16) {
RoomAvatarImage(avatar: spaceRoomProxy.avatar,
@@ -24,7 +22,7 @@ struct SpaceHeaderView: View {
.accessibilityHidden(true)
VStack(spacing: 8) {
Text(title)
Text(spaceRoomProxy.computedName)
.font(.compound.headingLGBold)
.foregroundStyle(.compound.textPrimary)
.multilineTextAlignment(.center)
@@ -76,31 +74,24 @@ struct SpaceHeaderView: View {
}
var spaceDetailsVisibilityTitle: String {
switch spaceRoomProxy.joinRule {
case .public:
L10n.commonPublicSpace
case .restricted(let rules), .knockRestricted(let rules):
// FIXME: Get this from the rule (falling back to a passed in parent??)
"<Parent name> space"
case .invite, .knock, .private, .custom, .none:
L10n.commonPrivateSpace
switch spaceRoomProxy.visibility {
case .public: L10n.commonPublicSpace
case .private: L10n.commonPrivateSpace
case .restricted(let parentName): L10n.screenSpaceListParentSpace(parentName)
case .none: L10n.commonPrivateSpace
}
}
var spaceDetailsVisibilityIcon: KeyPath<CompoundIcons, Image> {
switch spaceRoomProxy.joinRule {
case .public:
\.public
case .restricted, .knockRestricted:
\.space
case .invite, .knock, .private, .custom, .none:
\.lock
switch spaceRoomProxy.visibility {
case .public: \.public
case .private: \.lock
case .restricted: \.space
case .none: \.lock
}
}
}
import MatrixRustSDK
struct SpaceHeaderMembersView: View {
let heroes: [UserProfileProxy]
let joinedCount: Int
@@ -159,6 +150,8 @@ struct SpaceHeaderMembersView: View {
}
}
// MARK: - Previews
struct SpaceHeaderView_Previews: PreviewProvider, TestablePreview {
static let mediaProvider = MediaProviderMock(configuration: .init())
@@ -190,6 +183,7 @@ struct SpaceHeaderView_Previews: PreviewProvider, TestablePreview {
SpaceRoomProxyMock(.init(id: "!space3:matrix.org",
name: "Subspace",
isSpace: true,
parent: SpaceRoomProxyMock(.init(name: "Foundation", isSpace: true)),
childrenCount: 30,
joinedMembersCount: 123,
heroes: [.mockDan, .mockBob, .mockCharlie, .mockVerbose],
@@ -25,12 +25,26 @@ struct SpaceRoomCell: View {
private var subtitle: String {
if spaceRoomProxy.isSpace {
spaceRoomProxy.joinRule == .public ? L10n.commonPublicSpace : L10n.commonPrivateSpace
switch spaceRoomProxy.visibility {
case .public: L10n.commonPublicSpace
case .private: L10n.commonPrivateSpace
case .restricted(let parentName): L10n.screenSpaceListParentSpace(parentName)
case .none: L10n.commonPrivateSpace
}
} else {
L10n.commonMemberCount(spaceRoomProxy.joinedMembersCount)
}
}
var visibilityIcon: KeyPath<CompoundIcons, Image>? {
switch spaceRoomProxy.visibility {
case .public: \.public
case .private: \.lockSolid
case .restricted: nil
case .none: \.lockSolid
}
}
private var details: String {
if spaceRoomProxy.isSpace {
L10n.screenSpaceListDetails(L10n.commonRooms(spaceRoomProxy.childrenCount),
@@ -77,12 +91,12 @@ struct SpaceRoomCell: View {
private var content: some View {
HStack(spacing: 16) {
VStack(alignment: .leading, spacing: 2) {
Text(spaceRoomProxy.name ?? spaceRoomProxy.id)
Text(spaceRoomProxy.computedName)
.font(.compound.bodyLGSemibold)
.foregroundColor(.compound.textPrimary)
.lineLimit(1)
visibilityLabel
subtitleLabel
Text(details)
.font(.compound.bodyMD)
@@ -95,17 +109,19 @@ struct SpaceRoomCell: View {
}
}
private var visibilityLabel: some View {
private var subtitleLabel: some View {
Label {
Text(subtitle)
.font(.compound.bodyMD)
.foregroundStyle(.compound.textSecondary)
.lineLimit(1)
} icon: {
CompoundIcon(spaceRoomProxy.joinRule == .public ? \.public : \.lockSolid,
size: .xSmall,
relativeTo: .compound.bodyMD)
.foregroundStyle(.compound.iconTertiary)
if let visibilityIcon {
CompoundIcon(visibilityIcon,
size: .xSmall,
relativeTo: .compound.bodyMD)
.foregroundStyle(.compound.iconTertiary)
}
}
.labelStyle(.custom(spacing: 4))
}
@@ -68,7 +68,7 @@ class SpaceListScreenViewModel: SpaceListScreenViewModelType, SpaceListScreenVie
// MARK: - Private
private func selectSpace(_ spaceRoomProxy: SpaceRoomProxyProtocol) async {
switch await spaceServiceProxy.spaceRoomList(spaceID: spaceRoomProxy.id) {
switch await spaceServiceProxy.spaceRoomList(spaceID: spaceRoomProxy.id, parent: nil) {
case .success(let spaceRoomListProxy):
actionsSubject.send(.selectSpace(spaceRoomListProxy))
case .failure(let error):
@@ -11,6 +11,7 @@ import SwiftUI
typealias SpaceScreenViewModelType = StateStoreViewModelV2<SpaceScreenViewState, SpaceScreenViewAction>
class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtocol {
private let spaceRoomListProxy: SpaceRoomListProxyProtocol
private let spaceServiceProxy: SpaceServiceProxyProtocol
private let clientProxy: ClientProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol
@@ -25,6 +26,7 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
selectedSpaceRoomPublisher: CurrentValuePublisher<String?, Never>,
userSession: UserSessionProtocol,
userIndicatorController: UserIndicatorControllerProtocol) {
self.spaceRoomListProxy = spaceRoomListProxy
self.spaceServiceProxy = spaceServiceProxy
clientProxy = userSession.clientProxy
self.userIndicatorController = userIndicatorController
@@ -93,7 +95,7 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
state.joiningRoomIDs.insert(spaceRoomProxy.id)
defer { state.joiningRoomIDs.remove(spaceRoomProxy.id) }
guard case .success = await clientProxy.joinRoom(spaceRoomProxy.id, via: []) else {
guard case .success = await clientProxy.joinRoom(spaceRoomProxy.id, via: spaceRoomProxy.via) else {
showFailureIndicator()
return
}
@@ -109,7 +111,7 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc
}
private func selectSpace(_ spaceRoomProxy: SpaceRoomProxyProtocol) async {
switch await spaceServiceProxy.spaceRoomList(spaceID: spaceRoomProxy.id) {
switch await spaceServiceProxy.spaceRoomList(spaceID: spaceRoomProxy.id, parent: spaceRoomListProxy.spaceRoomProxy) {
case .success(let spaceRoomListProxy):
actionsSubject.send(.selectSpace(spaceRoomListProxy))
case .failure(let error):
@@ -68,6 +68,7 @@ struct SpaceScreen_Previews: PreviewProvider, TestablePreview {
let spaceRoomProxy = SpaceRoomProxyMock(.init(id: "!eng-space:matrix.org",
name: "Engineering Team",
isSpace: true,
parent: SpaceRoomProxyMock(.init(name: "MegaGroup", isSpace: true)),
childrenCount: 30,
joinedMembersCount: 76,
heroes: [.mockDan, .mockBob, .mockCharlie, .mockVerbose],
@@ -21,11 +21,12 @@ class SpaceRoomListProxy: SpaceRoomListProxyProtocol {
private let paginationStateHandle: TaskHandle
let paginationStatePublisher: CurrentValuePublisher<SpaceRoomListPaginationState, Never>
init(_ spaceRoomList: SpaceRoomListProtocol) throws {
// Parent is temporary until we get the restricted AllowRules from the server.
init(_ spaceRoomList: SpaceRoomListProtocol, parent: SpaceRoomProxyProtocol?) throws {
guard let spaceRoom = spaceRoomList.space() else { throw SpaceRoomListProxyError.missingSpace }
self.spaceRoomList = spaceRoomList
spaceRoomProxy = SpaceRoomProxy(spaceRoom: spaceRoom)
spaceRoomProxy = SpaceRoomProxy(spaceRoom: spaceRoom, parent: parent)
let paginationStateSubject = CurrentValueSubject<SpaceRoomListPaginationState, Never>(spaceRoomList.paginationState())
paginationStatePublisher = paginationStateSubject.asCurrentValuePublisher()
@@ -55,27 +56,27 @@ class SpaceRoomListProxy: SpaceRoomListProxyProtocol {
for update in updates {
switch update {
case .append(let spaceRooms):
rooms.append(contentsOf: spaceRooms.map(SpaceRoomProxy.init))
rooms.append(contentsOf: spaceRooms.map { SpaceRoomProxy(spaceRoom: $0, parent: spaceRoomProxy) })
case .clear:
rooms.removeAll()
case .pushFront(let spaceRoom):
rooms.insert(SpaceRoomProxy(spaceRoom: spaceRoom), at: 0)
rooms.insert(SpaceRoomProxy(spaceRoom: spaceRoom, parent: spaceRoomProxy), at: 0)
case .pushBack(let spaceRoom):
rooms.append(SpaceRoomProxy(spaceRoom: spaceRoom))
rooms.append(SpaceRoomProxy(spaceRoom: spaceRoom, parent: spaceRoomProxy))
case .popFront:
rooms.removeFirst()
case .popBack:
rooms.removeLast()
case .insert(let index, let spaceRoom):
rooms.insert(SpaceRoomProxy(spaceRoom: spaceRoom), at: Int(index))
rooms.insert(SpaceRoomProxy(spaceRoom: spaceRoom, parent: spaceRoomProxy), at: Int(index))
case .set(let index, let spaceRoom):
rooms[Int(index)] = SpaceRoomProxy(spaceRoom: spaceRoom)
rooms[Int(index)] = SpaceRoomProxy(spaceRoom: spaceRoom, parent: spaceRoomProxy)
case .remove(let index):
rooms.remove(at: Int(index))
case .truncate(let length):
rooms.removeSubrange(Int(length)..<rooms.count)
case .reset(let spaceRooms):
rooms = spaceRooms.map(SpaceRoomProxy.init)
rooms = spaceRooms.map { SpaceRoomProxy(spaceRoom: $0, parent: spaceRoomProxy) }
}
}
@@ -10,9 +10,15 @@ import MatrixRustSDK
class SpaceRoomProxy: SpaceRoomProxyProtocol {
private let spaceRoom: SpaceRoom
let parent: SpaceRoomProxyProtocol?
init(spaceRoom: SpaceRoom) {
/// Proxies a `SpaceRoom` from the Rust SDK.
/// - Parameters:
/// - spaceRoom: The `SpaceRoom` to proxy.
/// - parent: A temporary parameter until we get the `AllowRule`s from the server.
init(spaceRoom: SpaceRoom, parent: SpaceRoomProxyProtocol?) {
self.spaceRoom = spaceRoom
self.parent = parent
}
lazy var id = spaceRoom.roomId
@@ -20,6 +26,7 @@ class SpaceRoomProxy: SpaceRoomProxyProtocol {
var avatarURL: URL? { spaceRoom.avatarUrl.flatMap(URL.init) }
var isSpace: Bool { spaceRoom.roomType == .space }
var isDirect: Bool? { spaceRoom.isDirect }
var childrenCount: Int { Int(spaceRoom.childrenCount) }
var joinedMembersCount: Int { Int(spaceRoom.numJoinedMembers) }
@@ -31,4 +38,5 @@ class SpaceRoomProxy: SpaceRoomProxyProtocol {
var worldReadable: Bool? { spaceRoom.worldReadable }
var guestCanJoin: Bool { spaceRoom.guestCanJoin }
var state: Membership? { spaceRoom.state }
var via: [String] { spaceRoom.via }
}
@@ -8,6 +8,13 @@
import Foundation
import MatrixRustSDK
enum SpaceRoomProxyVisibility: Equatable {
case `public`
case `private`
case restricted(parentName: String)
// We can add the external case in here eventually.
}
// sourcery: AutoMockable
protocol SpaceRoomProxyProtocol {
var id: String { get }
@@ -15,6 +22,9 @@ protocol SpaceRoomProxyProtocol {
var avatarURL: URL? { get }
var isSpace: Bool { get }
var isDirect: Bool? { get }
/// A temporary property until we get the `AllowRule`s from the server.
var parent: SpaceRoomProxyProtocol? { get }
var childrenCount: Int { get }
var joinedMembersCount: Int { get }
@@ -26,14 +36,43 @@ protocol SpaceRoomProxyProtocol {
var worldReadable: Bool? { get }
var guestCanJoin: Bool { get }
var state: Membership? { get }
var via: [String] { get }
}
extension SpaceRoomProxyProtocol {
var avatar: RoomAvatar {
if isSpace {
.space(id: id, name: name, avatarURL: avatarURL)
} else { // We don't need to check for heroes, we only do that for DMs.
} else if isDirect == true, avatarURL == nil, heroes.count == 1 {
.heroes(heroes)
} else {
.room(id: id, name: name, avatarURL: avatarURL)
}
}
var computedName: String {
if !isSpace, isDirect == true, name == nil, heroes.count == 1, let dmRecipient = heroes.first {
dmRecipient.displayName ?? dmRecipient.id
} else {
name ?? canonicalAlias ?? id
}
}
var visibility: SpaceRoomProxyVisibility? {
switch joinRule {
case .public:
.public
case .restricted, .knockRestricted:
// Temporary solution until the server includes the `AllowRule` values (they're always empty right now).
if let parent {
.restricted(parentName: parent.computedName)
} else {
.private
}
case .invite, .knock, .private, .custom:
.private
case .none:
.none
}
}
}
@@ -30,9 +30,10 @@ class SpaceServiceProxy: SpaceServiceProxyProtocol {
})
}
func spaceRoomList(spaceID: String) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError> {
// The parent here is temporary until we get the restricted AllowRules from the server.
func spaceRoomList(spaceID: String, parent: SpaceRoomProxyProtocol?) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError> {
do {
return try await .success(SpaceRoomListProxy(spaceService.spaceRoomList(spaceId: spaceID)))
return try await .success(SpaceRoomListProxy(spaceService.spaceRoomList(spaceId: spaceID), parent: parent))
} catch {
MXLog.error("Failed creating space room list for \(spaceID): \(error)")
return .failure(.sdkError(error))
@@ -47,27 +48,27 @@ class SpaceServiceProxy: SpaceServiceProxyProtocol {
for update in updates {
switch update {
case .append(let spaceRooms):
spaces.append(contentsOf: spaceRooms.map(SpaceRoomProxy.init))
spaces.append(contentsOf: spaceRooms.map { SpaceRoomProxy(spaceRoom: $0, parent: nil) })
case .clear:
spaces.removeAll()
case .pushFront(let spaceRoom):
spaces.insert(SpaceRoomProxy(spaceRoom: spaceRoom), at: 0)
spaces.insert(SpaceRoomProxy(spaceRoom: spaceRoom, parent: nil), at: 0)
case .pushBack(let spaceRoom):
spaces.append(SpaceRoomProxy(spaceRoom: spaceRoom))
spaces.append(SpaceRoomProxy(spaceRoom: spaceRoom, parent: nil))
case .popFront:
spaces.removeFirst()
case .popBack:
spaces.removeLast()
case .insert(let index, let spaceRoom):
spaces.insert(SpaceRoomProxy(spaceRoom: spaceRoom), at: Int(index))
spaces.insert(SpaceRoomProxy(spaceRoom: spaceRoom, parent: nil), at: Int(index))
case .set(let index, let spaceRoom):
spaces[Int(index)] = SpaceRoomProxy(spaceRoom: spaceRoom)
spaces[Int(index)] = SpaceRoomProxy(spaceRoom: spaceRoom, parent: nil)
case .remove(let index):
spaces.remove(at: Int(index))
case .truncate(let length):
spaces.removeSubrange(Int(length)..<spaces.count)
case .reset(let spaceRooms):
spaces = spaceRooms.map(SpaceRoomProxy.init)
spaces = spaceRooms.map { SpaceRoomProxy(spaceRoom: $0, parent: nil) }
}
}
@@ -16,5 +16,5 @@ enum SpaceServiceProxyError: Error {
protocol SpaceServiceProxyProtocol {
var joinedSpacesPublisher: CurrentValuePublisher<[SpaceRoomProxyProtocol], Never> { get }
func spaceRoomList(spaceID: String) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>
func spaceRoomList(spaceID: String, parent: SpaceRoomProxyProtocol?) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>
}