From 2eaf75cb8733f080599052b8d367357d53ebfc4c Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Thu, 1 Feb 2024 23:40:38 +0800 Subject: [PATCH] [ENG-1592] Correctly assign tab titles on desktop (#2030) * correctly set tab titles * use selectedTabIndex * empty tabId * prettier --- apps/desktop/src/App.tsx | 49 +++++++++++++++++--------------- apps/web/src/App.tsx | 1 + core/src/library/library.rs | 6 +--- interface/RoutingContext.tsx | 1 + interface/hooks/useRouteTitle.ts | 8 ++++-- interface/index.tsx | 2 ++ 6 files changed, 36 insertions(+), 31 deletions(-) diff --git a/apps/desktop/src/App.tsx b/apps/desktop/src/App.tsx index 2fcd935d1..57e45a480 100644 --- a/apps/desktop/src/App.tsx +++ b/apps/desktop/src/App.tsx @@ -80,18 +80,22 @@ const routes = createRoutes(platform, cache); function AppInner() { const [tabs, setTabs] = useState(() => [createTab()]); - const [tabIndex, setTabIndex] = useState(0); + const [selectedTabIndex, setSelectedTabIndex] = useState(0); + + const selectedTab = tabs[selectedTabIndex]!; function createTab() { const history = createMemoryHistory(); const router = createMemoryRouterWithHistory({ routes, history }); + const id = Math.random().toString(); + const dispose = router.subscribe((event) => { // we don't care about non-idle events as those are artifacts of form mutations + suspense if (event.navigation.state !== 'idle') return; setTabs((routers) => { - const index = routers.findIndex((r) => r.router === router); + const index = routers.findIndex((r) => r.id === id); if (index === -1) return routers; const routerAtIndex = routers[index]!; @@ -105,12 +109,12 @@ function AppInner() { : Math.max(routerAtIndex.maxIndex, history.index) }; - return [...routers]; + return [...routers] }); }); return { - id: Math.random().toString(), + id, router, history, dispose, @@ -121,8 +125,6 @@ function AppInner() { }; } - const tab = tabs[tabIndex]!; - const createTabPromise = useRef(Promise.resolve()); const ref = useRef(null); @@ -131,38 +133,37 @@ function AppInner() { const div = ref.current; if (!div) return; - div.appendChild(tab.element); + div.appendChild(selectedTab.element); return () => { while (div.firstChild) { div.removeChild(div.firstChild); } }; - }, [tab.element]); + }, [selectedTab.element]); return ( ({ - setTitle(title) { - setTabs((oldTabs) => { - const tabs = [...oldTabs]; - const tab = tabs[tabIndex]; - if (!tab) return tabs; + setTitle(id, title) { + setTabs((tabs) => { + const tabIndex = tabs.findIndex(t => t.id === id); + if (tabIndex === -1) return tabs; - tabs[tabIndex] = { ...tab, title }; + tabs[tabIndex] = { ...tabs[tabIndex]!, title }; - return tabs; + return [...tabs]; }); } }), - [tabIndex] + [] )} > ({ router, title })), createTab() { createTabPromise.current = createTabPromise.current.then( @@ -170,9 +171,10 @@ function AppInner() { new Promise((res) => { startTransition(() => { setTabs((tabs) => { - const newTabs = [...tabs, createTab()]; + const newTab = createTab(); + const newTabs = [...tabs, newTab]; - setTabIndex(newTabs.length - 1); + setSelectedTabIndex(newTabs.length - 1); return newTabs; }); @@ -192,7 +194,7 @@ function AppInner() { tabs.splice(index, 1); - setTabIndex(Math.min(tabIndex, tabs.length - 1)); + setSelectedTabIndex(Math.min(selectedTabIndex, tabs.length - 1)); return [...tabs]; }); @@ -201,15 +203,16 @@ function AppInner() { }} > - {tabs.map((tab) => + {tabs.map((tab, index) => createPortal( , diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 0f9176b1d..14ceddc46 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -112,6 +112,7 @@ function App() { ; } | null>(null); diff --git a/interface/hooks/useRouteTitle.ts b/interface/hooks/useRouteTitle.ts index f975755cf..4c1b2a5ea 100644 --- a/interface/hooks/useRouteTitle.ts +++ b/interface/hooks/useRouteTitle.ts @@ -1,17 +1,19 @@ import { createContext, useContext, useLayoutEffect } from 'react'; +import { useRoutingContext } from '~/RoutingContext'; export function useRouteTitle(title: string) { + const routingCtx = useRoutingContext(); const ctx = useContext(RouteTitleContext); // layout effect avoids 'New Tab' showing up briefly useLayoutEffect(() => { document.title = title; - if (ctx) ctx.setTitle(title); - }, [title, ctx]); + if (ctx) ctx.setTitle(routingCtx.tabId, title); + }, [routingCtx.tabId, title, ctx]); return title; } export const RouteTitleContext = createContext<{ - setTitle: (title: string) => void; + setTitle: (id: string, title: string) => void; } | null>(null); diff --git a/interface/index.tsx b/interface/index.tsx index 19c329021..629a393bf 100644 --- a/interface/index.tsx +++ b/interface/index.tsx @@ -53,6 +53,7 @@ export function SpacedriveRouterProvider(props: { visible: boolean; router: Router; currentIndex: number; + tabId: string; maxIndex: number; }; }) { @@ -63,6 +64,7 @@ export function SpacedriveRouterProvider(props: { routes: props.routing.routes, visible: props.routing.visible, currentIndex: props.routing.currentIndex, + tabId: props.routing.tabId, maxIndex: props.routing.maxIndex }} >