web: switch to first matching space when opening room

Fixes #582
This commit is contained in:
Tulir Asokan 2025-01-03 12:12:33 +02:00
parent 39cb5f28a0
commit 5d25d839f8
4 changed files with 44 additions and 13 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 { DirectChatSpace, RoomListFilter, SpaceEdgeStore, SpaceOrphansSpace, UnreadsSpace } from "./space.ts" import { DirectChatSpace, RoomListFilter, Space, SpaceEdgeStore, SpaceOrphansSpace, UnreadsSpace } from "./space.ts"
export interface RoomListEntry { export interface RoomListEntry {
room_id: RoomID room_id: RoomID
@ -128,6 +128,22 @@ export class StateStore {
return null return null
} }
findMatchingSpace(room: RoomListEntry): Space | null {
if (this.spaceOrphans.include(room)) {
return this.spaceOrphans
}
for (const spaceID of this.topLevelSpaces.current) {
const space = this.spaceEdges.get(spaceID)
if (space?.include(room)) {
return space
}
}
if (this.directChatsSpace.include(room)) {
return this.directChatsSpace
}
return null
}
get roomListFilterFunc(): ((entry: RoomListEntry) => boolean) | null { get roomListFilterFunc(): ((entry: RoomListEntry) => boolean) | null {
if (!this.currentRoomListFilter && !this.currentRoomListQuery) { if (!this.currentRoomListFilter && !this.currentRoomListQuery) {
return null return null

View file

@ -96,12 +96,17 @@ class ContextFields implements MainScreenContextFields {
} }
} }
setActiveRoom = (roomID: RoomID | null, previewMeta?: Partial<RoomPreviewProps>, pushState = true) => { setActiveRoom = (
roomID: RoomID | null,
previewMeta?: Partial<RoomPreviewProps>,
toSpace?: RoomListFilter,
pushState = true,
) => {
console.log("Switching to room", roomID) console.log("Switching to room", roomID)
if (roomID) { if (roomID) {
const room = this.client.store.rooms.get(roomID) const room = this.client.store.rooms.get(roomID)
if (room) { if (room) {
this.#setActiveRoom(room, pushState) this.#setActiveRoom(room, toSpace, pushState)
} else { } else {
this.#setPreviewRoom(roomID, pushState, previewMeta) this.#setPreviewRoom(roomID, pushState, previewMeta)
} }
@ -151,10 +156,21 @@ class ContextFields implements MainScreenContextFields {
return room.preferences.room_window_title.replace("$room", name!) return room.preferences.room_window_title.replace("$room", name!)
} }
#setActiveRoom(room: RoomStateStore, pushState: boolean) { #setActiveRoom(room: RoomStateStore, space: RoomListFilter | undefined | null, pushState: boolean) {
window.activeRoom = room window.activeRoom = room
this.directSetActiveRoom(room) this.directSetActiveRoom(room)
this.directSetRightPanel(null) this.directSetRightPanel(null)
if (!space && this.client.store.currentRoomListFilter) {
const roomListEntry = this.client.store.roomListEntries.get(room.roomID)
if (roomListEntry && !this.client.store.currentRoomListFilter.include(roomListEntry)) {
space = this.client.store.findMatchingSpace(roomListEntry)
}
}
if (space && space !== this.client.store.currentRoomListFilter) {
console.log("Switching to space", space?.id)
this.directSetSpace(space)
this.client.store.currentRoomListFilter = space
}
this.rightPanelStack = [] this.rightPanelStack = []
this.client.store.activeRoomID = room.roomID this.client.store.activeRoomID = room.roomID
this.client.store.activeRoomIsPreview = false this.client.store.activeRoomIsPreview = false
@ -168,7 +184,7 @@ class ContextFields implements MainScreenContextFields {
.querySelector(`div.room-entry[data-room-id="${CSS.escape(room.roomID)}"]`) .querySelector(`div.room-entry[data-room-id="${CSS.escape(room.roomID)}"]`)
?.scrollIntoView({ block: "nearest" }) ?.scrollIntoView({ block: "nearest" })
if (pushState) { if (pushState) {
history.pushState({ room_id: room.roomID, space_id: history.state?.space_id }, "") history.pushState({ room_id: room.roomID, space_id: space?.id ?? history.state?.space_id }, "")
} }
let roomNameForTitle = room.meta.current.name let roomNameForTitle = room.meta.current.name
if (roomNameForTitle && roomNameForTitle.length > 48) { if (roomNameForTitle && roomNameForTitle.length > 48) {
@ -217,7 +233,7 @@ class ContextFields implements MainScreenContextFields {
const SYNC_ERROR_HIDE_DELAY = 30 * 1000 const SYNC_ERROR_HIDE_DELAY = 30 * 1000
const handleURLHash = (client: Client) => { const handleURLHash = (client: Client, context: MainScreenContextFields) => {
if (!location.hash.startsWith("#/uri/")) { if (!location.hash.startsWith("#/uri/")) {
if (location.search) { if (location.search) {
const currentETag = ( const currentETag = (
@ -268,7 +284,7 @@ const handleURLHash = (client: Client) => {
// TODO loading indicator or something for this? // TODO loading indicator or something for this?
client.rpc.resolveAlias(uri.identifier).then( client.rpc.resolveAlias(uri.identifier).then(
res => { res => {
window.mainScreenContext.setActiveRoom(res.room_id, { context.setActiveRoom(res.room_id, {
alias: uri.identifier, alias: uri.identifier,
via: res.servers.slice(0, 3), via: res.servers.slice(0, 3),
}) })
@ -321,13 +337,13 @@ const MainScreen = () => {
context.setActiveRoom(roomID, { context.setActiveRoom(roomID, {
alias: ensureString(evt.state?.source_alias) || undefined, alias: ensureString(evt.state?.source_alias) || undefined,
via: ensureStringArray(evt.state?.source_via), via: ensureStringArray(evt.state?.source_via),
}, false) }, undefined, false)
} }
context.setRightPanel(evt.state?.right_panel ?? null, false) context.setRightPanel(evt.state?.right_panel ?? null, false)
} }
window.addEventListener("popstate", listener) window.addEventListener("popstate", listener)
const initHandle = () => { const initHandle = () => {
const state = handleURLHash(client) const state = handleURLHash(client, context)
listener({ state } as PopStateEvent) listener({ state } as PopStateEvent)
} }
let cancel = () => {} let cancel = () => {}

View file

@ -13,14 +13,14 @@
// //
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import { createContext } from "react" import React, { createContext } from "react"
import { RoomListFilter } from "@/api/statestore" import { RoomListFilter } from "@/api/statestore"
import type { RoomID } from "@/api/types" import type { RoomID } from "@/api/types"
import type { RightPanelProps } from "./rightpanel/RightPanel.tsx" import type { RightPanelProps } from "./rightpanel/RightPanel.tsx"
import type { RoomPreviewProps } from "./roomview/RoomPreview.tsx" import type { RoomPreviewProps } from "./roomview/RoomPreview.tsx"
export interface MainScreenContextFields { export interface MainScreenContextFields {
setActiveRoom: (roomID: RoomID | null, previewMeta?: Partial<RoomPreviewProps>) => void setActiveRoom: (roomID: RoomID | null, previewMeta?: Partial<RoomPreviewProps>, toSpace?: RoomListFilter) => void
setSpace: (space: RoomListFilter | null, pushState?: boolean) => void setSpace: (space: RoomListFilter | null, pushState?: boolean) => void
clickRoom: (evt: React.MouseEvent) => void clickRoom: (evt: React.MouseEvent) => void
clearActiveRoom: () => void clearActiveRoom: () => void

View file

@ -77,8 +77,7 @@ const RoomList = ({ activeRoomID, space }: RoomListProps) => {
for (let i = client.store.roomList.current.length - 1; i >= 0; i--) { for (let i = client.store.roomList.current.length - 1; i >= 0; i--) {
const entry = client.store.roomList.current[i] const entry = client.store.roomList.current[i]
if (entry[wantedField] > 0 && space.include(entry)) { if (entry[wantedField] > 0 && space.include(entry)) {
mainScreen.setActiveRoom(entry.room_id) mainScreen.setActiveRoom(entry.room_id, undefined, space)
mainScreen.setSpace(space)
evt.stopPropagation() evt.stopPropagation()
break break
} }