Improve drawer navigation

This commit is contained in:
hzrd149 2023-10-23 11:39:39 -05:00
parent 0f1e677459
commit 7b03925054
2 changed files with 75 additions and 24 deletions

View File

@ -0,0 +1,5 @@
---
"nostrudel": patch
---
Improve drawer navigation

View File

@ -1,3 +1,4 @@
import { PropsWithChildren, createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
import {
ButtonGroup,
Drawer,
@ -9,13 +10,13 @@ import {
DrawerProps,
IconButton,
} from "@chakra-ui/react";
import { PropsWithChildren, createContext, useCallback, useContext, useMemo, useState } from "react";
import { RouteObject, RouterProvider, To, createMemoryRouter, useNavigate } from "react-router-dom";
import { Location, RouteObject, RouterProvider, To, createMemoryRouter, useNavigate } from "react-router-dom";
import { ErrorBoundary } from "../components/error-boundary";
import NoteView from "../views/note";
import { ChevronLeftIcon, ChevronRightIcon, ExternalLinkIcon } from "../components/icons";
import { PageProviders } from ".";
import { logger } from "../helpers/debug";
type Router = ReturnType<typeof createMemoryRouter>;
@ -82,40 +83,85 @@ export function useNavigateInDrawer() {
return isInDrawer ? navigate : openDrawer;
}
const log = logger.extend("DrawerRouter");
export default function DrawerSubViewProvider({
children,
parentRouter,
}: PropsWithChildren & { parentRouter: Router }) {
const [router, setRouter] = useState<Router | null>(null);
const openInParent = useCallback(
const openInParent = useCallback((to: To) => parentRouter.navigate(to), [parentRouter]);
const direction = useRef<"up" | "down">();
const marker = useRef<number>(0);
useEffect(() => {
return parentRouter.subscribe((event) => {
const location = event.location as Location<{ subRouterPath?: To | null } | null>;
const subRoute = location.state?.subRouterPath;
if (event.historyAction === "PUSH") marker.current++;
else if (event.historyAction === "POP") marker.current--;
if (subRoute) {
if (router) {
if (router.state.location.pathname !== subRoute && direction.current !== "up") {
log("Updating router from parent state");
direction.current = "down";
router.navigate(subRoute);
direction.current = undefined;
}
} else {
log("Create Router");
const newRouter = createMemoryRouter(routes, { initialEntries: [subRoute] });
newRouter.subscribe((e) => {
if (e.errors && e.errors[0].status === 404 && e.errors[0].internal) {
openInParent(e.location);
} else if (direction.current !== "down") {
log("Updating parent state from Router");
direction.current = "up";
parentRouter.navigate(".", {
state: { ...parentRouter.state.location.state, subRouterPath: e.location.pathname },
});
}
direction.current = undefined;
});
// use the parent routers createHref method so that users can open links in new tabs
newRouter.createHref = parentRouter.createHref;
setRouter(newRouter);
}
} else if (router) {
log("Destroy Router");
setRouter(null);
}
});
}, [parentRouter, router, setRouter]);
const openDrawer = useCallback(
(to: To) => {
parentRouter.navigate(to);
setRouter(null);
marker.current = 0;
parentRouter.navigate(".", { state: { ...parentRouter.state.location.state, subRouterPath: to } });
},
[parentRouter],
);
const openDrawer = useCallback(
(to: To) => {
const newRouter = createMemoryRouter(routes, { initialEntries: [to] });
newRouter.subscribe((e) => {
if (e.errors && e.errors[0].status === 404 && e.errors[0].internal) {
openInParent(e.location);
}
});
// use the parent routers createHref method so that users can open links in new tabs
newRouter.createHref = parentRouter.createHref;
setRouter(newRouter);
},
[setRouter, openInParent],
);
const closeDrawer = useCallback(() => {
setRouter(null);
}, [setRouter]);
const i = marker.current;
if (i > 0) {
log(`Navigating back ${i} entries to the point the drawer was opened`);
parentRouter.navigate(-i);
} else {
log(`Failed to navigate back, clearing state`);
parentRouter.navigate(".", { state: { ...parentRouter.state.location.state, subRouterPath: undefined } });
}
// reset marker
marker.current = 0;
}, [parentRouter]);
const context = useMemo(
() => ({