1
0
Fork 0
forked from Mirrors/gomuks

web/roomlist: add pseudo-space for space orphans

This commit is contained in:
Tulir Asokan 2024-12-29 14:55:18 +02:00
parent 6b01dec307
commit f4a778ecbb
3 changed files with 43 additions and 12 deletions

View file

@ -39,7 +39,7 @@ import {
} from "../types" } from "../types"
import { InvitedRoomStore } from "./invitedroom.ts" import { InvitedRoomStore } from "./invitedroom.ts"
import { RoomStateStore } from "./room.ts" import { RoomStateStore } from "./room.ts"
import { RoomListFilter, SpaceEdgeStore } from "./space.ts" import { RoomListFilter, SpaceEdgeStore, SpaceOrphansSpace } from "./space.ts"
export interface RoomListEntry { export interface RoomListEntry {
room_id: RoomID room_id: RoomID
@ -75,6 +75,7 @@ export class StateStore {
readonly roomList = new NonNullCachedEventDispatcher<RoomListEntry[]>([]) readonly roomList = new NonNullCachedEventDispatcher<RoomListEntry[]>([])
readonly topLevelSpaces = new NonNullCachedEventDispatcher<RoomID[]>([]) readonly topLevelSpaces = new NonNullCachedEventDispatcher<RoomID[]>([])
readonly spaceEdges: Map<RoomID, SpaceEdgeStore> = new Map() readonly spaceEdges: Map<RoomID, SpaceEdgeStore> = new Map()
readonly spaceOrphans = new SpaceOrphansSpace(this)
currentRoomListQuery: string = "" currentRoomListQuery: string = ""
currentRoomListFilter: RoomListFilter | null = null currentRoomListFilter: RoomListFilter | null = null
readonly accountData: Map<string, UnknownEventContent> = new Map() readonly accountData: Map<string, UnknownEventContent> = new Map()
@ -277,11 +278,18 @@ export class StateStore {
if (updatedRoomList) { if (updatedRoomList) {
this.roomList.emit(updatedRoomList) this.roomList.emit(updatedRoomList)
} }
for (const [spaceID, children] of Object.entries(sync.space_edges ?? {})) { if (sync.space_edges) {
this.getSpaceStore(spaceID, true).children = children // Ensure all space stores exist first
for (const spaceID of Object.keys(sync.space_edges)) {
this.getSpaceStore(spaceID, true)
}
for (const [spaceID, children] of Object.entries(sync.space_edges ?? {})) {
this.getSpaceStore(spaceID, true).children = children
}
} }
if (sync.top_level_spaces) { if (sync.top_level_spaces) {
this.topLevelSpaces.emit(sync.top_level_spaces) this.topLevelSpaces.emit(sync.top_level_spaces)
this.spaceOrphans.children = sync.top_level_spaces.map(child_id => ({ child_id }))
} }
} }

View file

@ -44,11 +44,11 @@ export class SpaceEdgeStore implements RoomListFilter {
constructor(public id: RoomID, private parent: StateStore) { constructor(public id: RoomID, private parent: StateStore) {
} }
addParent(parent: SpaceEdgeStore) { #addParent(parent: SpaceEdgeStore) {
this.#parentSpaces.add(parent) this.#parentSpaces.add(parent)
} }
removeParent(parent: SpaceEdgeStore) { #removeParent(parent: SpaceEdgeStore) {
this.#parentSpaces.delete(parent) this.#parentSpaces.delete(parent)
} }
@ -95,28 +95,42 @@ export class SpaceEdgeStore implements RoomListFilter {
const spaceStore = this.parent.getSpaceStore(child.child_id) const spaceStore = this.parent.getSpaceStore(child.child_id)
if (spaceStore) { if (spaceStore) {
newChildSpaces.add(spaceStore) newChildSpaces.add(spaceStore)
spaceStore.addParent(this) spaceStore.#addParent(this)
} else { } else {
newChildRooms.add(child.child_id) newChildRooms.add(child.child_id)
} }
} }
for (const space of this.#childSpaces) { for (const space of this.#childSpaces) {
if (!newChildSpaces.has(space)) { if (!newChildSpaces.has(space)) {
space.removeParent(this) space.#removeParent(this)
} }
} }
const addedRooms = newChildRooms.difference(this.#childRooms) const addedRooms = newChildRooms.difference(this.#childRooms)
const removedRooms = this.#childRooms.difference(newChildRooms) const removedRooms = this.#childRooms.difference(newChildRooms)
const didAddChildren = newChildSpaces.difference(this.#childSpaces).size > 0
const recalculateFlattened = removedRooms.size > 0 || didAddChildren
this.#children = newChildren this.#children = newChildren
this.#childRooms = newChildRooms this.#childRooms = newChildRooms
this.#childSpaces = newChildSpaces this.#childSpaces = newChildSpaces
if (this.#childSpaces.size > 0) { if (this.#childSpaces.size > 0) {
this.#updateFlattened(removedRooms.size > 0, addedRooms) this.#updateFlattened(recalculateFlattened, addedRooms)
} else { } else {
this.#flattenedRooms = newChildRooms this.#flattenedRooms = newChildRooms
} }
if (this.#parentSpaces.size > 0) { if (this.#parentSpaces.size > 0) {
this.#notifyParentsOfChange(removedRooms.size > 0, addedRooms, new WeakSet()) this.#notifyParentsOfChange(recalculateFlattened, addedRooms, new WeakSet())
} }
} }
} }
export class SpaceOrphansSpace extends SpaceEdgeStore {
static id = "fi.mau.gomuks.space_orphans"
constructor(parent: StateStore) {
super(SpaceOrphansSpace.id, parent)
}
include(room: RoomListEntry): boolean {
return !super.include(room) && !room.dm_user_id
}
}

View file

@ -75,6 +75,12 @@ const RoomList = ({ activeRoomID }: RoomListProps) => {
} }
const roomListFilter = client.store.roomListFilterFunc const roomListFilter = client.store.roomListFilterFunc
const pseudoSpaces = [
null,
DirectChatSpace,
UnreadsSpace,
client.store.spaceOrphans,
]
return <div className="room-list-wrapper"> return <div className="room-list-wrapper">
<div className="room-search-wrapper"> <div className="room-search-wrapper">
<input <input
@ -92,9 +98,12 @@ const RoomList = ({ activeRoomID }: RoomListProps) => {
</button> </button>
</div> </div>
<div className="space-bar"> <div className="space-bar">
<FakeSpace space={null} setSpace={setSpace} isActive={space === null} /> {pseudoSpaces.map(pseudoSpace => <FakeSpace
<FakeSpace space={DirectChatSpace} setSpace={setSpace} isActive={space?.id === DirectChatSpace.id} /> key={pseudoSpace?.id ?? "null"}
<FakeSpace space={UnreadsSpace} setSpace={setSpace} isActive={space?.id === UnreadsSpace.id} /> space={pseudoSpace}
setSpace={setSpace}
isActive={space?.id === pseudoSpace?.id}
/>)}
{spaces.map(roomID => <Space {spaces.map(roomID => <Space
roomID={roomID} roomID={roomID}
client={client} client={client}