mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-06 23:43:32 +00:00
[ENG-1592] Correctly assign tab titles on desktop (#2030)
* correctly set tab titles * use selectedTabIndex * empty tabId * prettier
This commit is contained in:
parent
8cbd38d6f9
commit
74d97d4957
|
@ -80,18 +80,22 @@ const routes = createRoutes(platform, cache);
|
||||||
|
|
||||||
function AppInner() {
|
function AppInner() {
|
||||||
const [tabs, setTabs] = useState(() => [createTab()]);
|
const [tabs, setTabs] = useState(() => [createTab()]);
|
||||||
const [tabIndex, setTabIndex] = useState(0);
|
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
|
||||||
|
|
||||||
|
const selectedTab = tabs[selectedTabIndex]!;
|
||||||
|
|
||||||
function createTab() {
|
function createTab() {
|
||||||
const history = createMemoryHistory();
|
const history = createMemoryHistory();
|
||||||
const router = createMemoryRouterWithHistory({ routes, history });
|
const router = createMemoryRouterWithHistory({ routes, history });
|
||||||
|
|
||||||
|
const id = Math.random().toString();
|
||||||
|
|
||||||
const dispose = router.subscribe((event) => {
|
const dispose = router.subscribe((event) => {
|
||||||
// we don't care about non-idle events as those are artifacts of form mutations + suspense
|
// we don't care about non-idle events as those are artifacts of form mutations + suspense
|
||||||
if (event.navigation.state !== 'idle') return;
|
if (event.navigation.state !== 'idle') return;
|
||||||
|
|
||||||
setTabs((routers) => {
|
setTabs((routers) => {
|
||||||
const index = routers.findIndex((r) => r.router === router);
|
const index = routers.findIndex((r) => r.id === id);
|
||||||
if (index === -1) return routers;
|
if (index === -1) return routers;
|
||||||
|
|
||||||
const routerAtIndex = routers[index]!;
|
const routerAtIndex = routers[index]!;
|
||||||
|
@ -105,12 +109,12 @@ function AppInner() {
|
||||||
: Math.max(routerAtIndex.maxIndex, history.index)
|
: Math.max(routerAtIndex.maxIndex, history.index)
|
||||||
};
|
};
|
||||||
|
|
||||||
return [...routers];
|
return [...routers]
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: Math.random().toString(),
|
id,
|
||||||
router,
|
router,
|
||||||
history,
|
history,
|
||||||
dispose,
|
dispose,
|
||||||
|
@ -121,8 +125,6 @@ function AppInner() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const tab = tabs[tabIndex]!;
|
|
||||||
|
|
||||||
const createTabPromise = useRef(Promise.resolve());
|
const createTabPromise = useRef(Promise.resolve());
|
||||||
|
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
@ -131,38 +133,37 @@ function AppInner() {
|
||||||
const div = ref.current;
|
const div = ref.current;
|
||||||
if (!div) return;
|
if (!div) return;
|
||||||
|
|
||||||
div.appendChild(tab.element);
|
div.appendChild(selectedTab.element);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
while (div.firstChild) {
|
while (div.firstChild) {
|
||||||
div.removeChild(div.firstChild);
|
div.removeChild(div.firstChild);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [tab.element]);
|
}, [selectedTab.element]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RouteTitleContext.Provider
|
<RouteTitleContext.Provider
|
||||||
value={useMemo(
|
value={useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
setTitle(title) {
|
setTitle(id, title) {
|
||||||
setTabs((oldTabs) => {
|
setTabs((tabs) => {
|
||||||
const tabs = [...oldTabs];
|
const tabIndex = tabs.findIndex(t => t.id === id);
|
||||||
const tab = tabs[tabIndex];
|
if (tabIndex === -1) return tabs;
|
||||||
if (!tab) return tabs;
|
|
||||||
|
|
||||||
tabs[tabIndex] = { ...tab, title };
|
tabs[tabIndex] = { ...tabs[tabIndex]!, title };
|
||||||
|
|
||||||
return tabs;
|
return [...tabs];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
[tabIndex]
|
[]
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<TabsContext.Provider
|
<TabsContext.Provider
|
||||||
value={{
|
value={{
|
||||||
tabIndex,
|
tabIndex: selectedTabIndex,
|
||||||
setTabIndex,
|
setTabIndex: setSelectedTabIndex,
|
||||||
tabs: tabs.map(({ router, title }) => ({ router, title })),
|
tabs: tabs.map(({ router, title }) => ({ router, title })),
|
||||||
createTab() {
|
createTab() {
|
||||||
createTabPromise.current = createTabPromise.current.then(
|
createTabPromise.current = createTabPromise.current.then(
|
||||||
|
@ -170,9 +171,10 @@ function AppInner() {
|
||||||
new Promise((res) => {
|
new Promise((res) => {
|
||||||
startTransition(() => {
|
startTransition(() => {
|
||||||
setTabs((tabs) => {
|
setTabs((tabs) => {
|
||||||
const newTabs = [...tabs, createTab()];
|
const newTab = createTab();
|
||||||
|
const newTabs = [...tabs, newTab];
|
||||||
|
|
||||||
setTabIndex(newTabs.length - 1);
|
setSelectedTabIndex(newTabs.length - 1);
|
||||||
|
|
||||||
return newTabs;
|
return newTabs;
|
||||||
});
|
});
|
||||||
|
@ -192,7 +194,7 @@ function AppInner() {
|
||||||
|
|
||||||
tabs.splice(index, 1);
|
tabs.splice(index, 1);
|
||||||
|
|
||||||
setTabIndex(Math.min(tabIndex, tabs.length - 1));
|
setSelectedTabIndex(Math.min(selectedTabIndex, tabs.length - 1));
|
||||||
|
|
||||||
return [...tabs];
|
return [...tabs];
|
||||||
});
|
});
|
||||||
|
@ -201,15 +203,16 @@ function AppInner() {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SpacedriveInterfaceRoot>
|
<SpacedriveInterfaceRoot>
|
||||||
{tabs.map((tab) =>
|
{tabs.map((tab, index) =>
|
||||||
createPortal(
|
createPortal(
|
||||||
<SpacedriveRouterProvider
|
<SpacedriveRouterProvider
|
||||||
key={tab.id}
|
key={tab.id}
|
||||||
routing={{
|
routing={{
|
||||||
routes,
|
routes,
|
||||||
visible: tabIndex === tabs.indexOf(tab),
|
visible: selectedTabIndex === tabs.indexOf(tab),
|
||||||
router: tab.router,
|
router: tab.router,
|
||||||
currentIndex: tab.currentIndex,
|
currentIndex: tab.currentIndex,
|
||||||
|
tabId: tab.id,
|
||||||
maxIndex: tab.maxIndex
|
maxIndex: tab.maxIndex
|
||||||
}}
|
}}
|
||||||
/>,
|
/>,
|
||||||
|
|
|
@ -112,6 +112,7 @@ function App() {
|
||||||
<SpacedriveRouterProvider
|
<SpacedriveRouterProvider
|
||||||
routing={{
|
routing={{
|
||||||
...router,
|
...router,
|
||||||
|
tabId: '',
|
||||||
routes,
|
routes,
|
||||||
visible: true
|
visible: true
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
use crate::{
|
use crate::{api::CoreEvent, object::media::thumbnail::get_indexed_thumbnail_path, sync, Node};
|
||||||
api::CoreEvent,
|
|
||||||
object::{media::thumbnail::get_indexed_thumbnail_path, orphan_remover::OrphanRemoverActor},
|
|
||||||
sync, Node,
|
|
||||||
};
|
|
||||||
|
|
||||||
use sd_file_path_helper::{file_path_to_full_path, IsolatedFilePathData};
|
use sd_file_path_helper::{file_path_to_full_path, IsolatedFilePathData};
|
||||||
use sd_p2p::spacetunnel::Identity;
|
use sd_p2p::spacetunnel::Identity;
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { createRoutes } from './app';
|
||||||
export const RoutingContext = createContext<{
|
export const RoutingContext = createContext<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
currentIndex: number;
|
currentIndex: number;
|
||||||
|
tabId: string;
|
||||||
maxIndex: number;
|
maxIndex: number;
|
||||||
routes: ReturnType<typeof createRoutes>;
|
routes: ReturnType<typeof createRoutes>;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
import { createContext, useContext, useLayoutEffect } from 'react';
|
import { createContext, useContext, useLayoutEffect } from 'react';
|
||||||
|
import { useRoutingContext } from '~/RoutingContext';
|
||||||
|
|
||||||
export function useRouteTitle(title: string) {
|
export function useRouteTitle(title: string) {
|
||||||
|
const routingCtx = useRoutingContext();
|
||||||
const ctx = useContext(RouteTitleContext);
|
const ctx = useContext(RouteTitleContext);
|
||||||
|
|
||||||
// layout effect avoids 'New Tab' showing up briefly
|
// layout effect avoids 'New Tab' showing up briefly
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
document.title = title;
|
document.title = title;
|
||||||
if (ctx) ctx.setTitle(title);
|
if (ctx) ctx.setTitle(routingCtx.tabId, title);
|
||||||
}, [title, ctx]);
|
}, [routingCtx.tabId, title, ctx]);
|
||||||
|
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RouteTitleContext = createContext<{
|
export const RouteTitleContext = createContext<{
|
||||||
setTitle: (title: string) => void;
|
setTitle: (id: string, title: string) => void;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
|
@ -53,6 +53,7 @@ export function SpacedriveRouterProvider(props: {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
router: Router;
|
router: Router;
|
||||||
currentIndex: number;
|
currentIndex: number;
|
||||||
|
tabId: string;
|
||||||
maxIndex: number;
|
maxIndex: number;
|
||||||
};
|
};
|
||||||
}) {
|
}) {
|
||||||
|
@ -63,6 +64,7 @@ export function SpacedriveRouterProvider(props: {
|
||||||
routes: props.routing.routes,
|
routes: props.routing.routes,
|
||||||
visible: props.routing.visible,
|
visible: props.routing.visible,
|
||||||
currentIndex: props.routing.currentIndex,
|
currentIndex: props.routing.currentIndex,
|
||||||
|
tabId: props.routing.tabId,
|
||||||
maxIndex: props.routing.maxIndex
|
maxIndex: props.routing.maxIndex
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
Loading…
Reference in a new issue