diff --git a/web/src/ui/MainScreen.tsx b/web/src/ui/MainScreen.tsx index 5434682..cbe4d30 100644 --- a/web/src/ui/MainScreen.tsx +++ b/web/src/ui/MainScreen.tsx @@ -13,7 +13,7 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { JSX, use, useEffect, useInsertionEffect, useLayoutEffect, useMemo, useReducer, useState } from "react" +import { JSX, use, useEffect, useInsertionEffect, useLayoutEffect, useMemo, useState } from "react" import { SyncLoader } from "react-spinners" import Client from "@/api/client.ts" import { RoomStateStore } from "@/api/statestore" @@ -43,18 +43,12 @@ function objectIsEqual(a: RightPanelProps | null, b: RightPanelProps | null): bo return true } -const rpReducer = (prevState: RightPanelProps | null, newState: RightPanelProps | null) => { - if (objectIsEqual(prevState, newState)) { - return null - } - return newState -} - class ContextFields implements MainScreenContextFields { public keybindings: Keybindings + private rightPanelStack: RightPanelProps[] = [] constructor( - public setRightPanel: (props: RightPanelProps | null) => void, + private directSetRightPanel: (props: RightPanelProps | null) => void, private directSetActiveRoom: (room: RoomStateStore | null) => void, private client: Client, ) { @@ -62,12 +56,47 @@ class ContextFields implements MainScreenContextFields { client.store.switchRoom = this.setActiveRoom } + get currentRightPanel(): RightPanelProps | null { + return this.rightPanelStack.length ? this.rightPanelStack[this.rightPanelStack.length-1] : null + } + + setRightPanel = (props: RightPanelProps | null, pushState = true) => { + const isEqual = objectIsEqual(this.currentRightPanel, props) + if (isEqual && !pushState) { + return + } + if (isEqual || props === null) { + const length = this.rightPanelStack.length + this.rightPanelStack = [] + this.directSetRightPanel(null) + if (length && pushState) { + history.go(-length) + } + } else { + this.directSetRightPanel(props) + for (let i = this.rightPanelStack.length - 1; i >= 0; i--) { + if (objectIsEqual(this.rightPanelStack[i], props)) { + this.rightPanelStack = this.rightPanelStack.slice(0, i + 1) + if (pushState) { + history.go(i - this.rightPanelStack.length) + } + return + } + } // else: + this.rightPanelStack.push(props) + if (pushState) { + history.pushState({ ...(history.state ?? {}), right_panel: props }, "") + } + } + } + setActiveRoom = (roomID: RoomID | null, pushState = true) => { console.log("Switching to room", roomID) const room = (roomID && this.client.store.rooms.get(roomID)) || null window.activeRoom = room this.directSetActiveRoom(room) - this.setRightPanel(null) + this.directSetRightPanel(null) + this.rightPanelStack = [] this.client.store.activeRoomID = room?.roomID ?? null this.keybindings.activeRoom = room if (room) { @@ -113,11 +142,11 @@ const SYNC_ERROR_HIDE_DELAY = 30 * 1000 const MainScreen = () => { const [activeRoom, directSetActiveRoom] = useState(null) - const [rightPanel, setRightPanel] = useReducer(rpReducer, null) + const [rightPanel, directSetRightPanel] = useState(null) const client = use(ClientContext)! const syncStatus = useEventAsState(client.syncStatus) const context = useMemo( - () => new ContextFields(setRightPanel, directSetActiveRoom, client), + () => new ContextFields(directSetRightPanel, directSetActiveRoom, client), [client], ) useLayoutEffect(() => { @@ -129,6 +158,7 @@ const MainScreen = () => { if (roomID !== client.store.activeRoomID) { context.setActiveRoom(roomID, false) } + context.setRightPanel(evt.state?.right_panel ?? null, false) } window.addEventListener("popstate", listener) return () => window.removeEventListener("popstate", listener)