diff --git a/.vscode/settings.json b/.vscode/settings.json index 3659a64..fae8e3d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,4 @@ { "typescript.tsdk": "node_modules/typescript/lib", - "typescript.enablePromptUseWorkspaceTsdk": true, - "explorer.sortOrder": "mixed" + "typescript.enablePromptUseWorkspaceTsdk": true } diff --git a/package-lock.json b/package-lock.json index c37c6e4..85ba4a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "leaflet": "^1.9.4", "next": "^14.0.3", "next-cookies": "^2.0.3", + "next-usequerystate": "^1.13.0", "react": "^18.2.0", "react-aria": "^3.21.0", "react-dom": "^18.2.0", @@ -8073,6 +8074,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" + }, "node_modules/mrmime": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", @@ -8178,6 +8184,17 @@ "universal-cookie": "^4.0.2" } }, + "node_modules/next-usequerystate": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/next-usequerystate/-/next-usequerystate-1.13.0.tgz", + "integrity": "sha512-5AeBzUqTcs+uNaQTYBNWNxpwvhsAOFlpJ2xKio3KDX4JVN/S2OwZGx2REw013Jx2D4yvkAyUMYOMtXRq+MVURA==", + "dependencies": { + "mitt": "^3.0.1" + }, + "peerDependencies": { + "next": ">=13.4 <14.0.2 || ^14.0.3" + } + }, "node_modules/next/node_modules/@swc/helpers": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", @@ -16783,6 +16800,11 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" }, + "mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" + }, "mrmime": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", @@ -16861,6 +16883,14 @@ "universal-cookie": "^4.0.2" } }, + "next-usequerystate": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/next-usequerystate/-/next-usequerystate-1.13.0.tgz", + "integrity": "sha512-5AeBzUqTcs+uNaQTYBNWNxpwvhsAOFlpJ2xKio3KDX4JVN/S2OwZGx2REw013Jx2D4yvkAyUMYOMtXRq+MVURA==", + "requires": { + "mitt": "^3.0.1" + } + }, "no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", diff --git a/package.json b/package.json index d08a662..57be87b 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "leaflet": "^1.9.4", "next": "^14.0.3", "next-cookies": "^2.0.3", + "next-usequerystate": "^1.13.0", "react": "^18.2.0", "react-aria": "^3.21.0", "react-dom": "^18.2.0", diff --git a/src/app/[lang]/crag/[cragSlug]/components/crag-routes.tsx b/src/app/[lang]/crag/[cragSlug]/components/crag-routes.tsx index ad37b17..843eccc 100644 --- a/src/app/[lang]/crag/[cragSlug]/components/crag-routes.tsx +++ b/src/app/[lang]/crag/[cragSlug]/components/crag-routes.tsx @@ -5,11 +5,21 @@ import { Route, Sector, } from "../../../../../graphql/generated"; -import { createContext, useCallback, useEffect, useRef, useState } from "react"; +import { + createContext, + useEffect, + useLayoutEffect, + useRef, + useState, +} from "react"; import CragRouteList from "./crag-routes/crag-route-list"; import CragSector from "./crag-routes/crag-sector"; import CragRoutesActions from "./crag-routes/crag-routes-actions"; -import { usePathname, useRouter, useSearchParams } from "next/navigation"; +import { + parseAsArrayOf, + parseAsInteger, + useQueryState, +} from "next-usequerystate"; interface Props { crag: Crag; @@ -247,80 +257,34 @@ function CragRoutes({ crag, mySummary }: Props) { }, [compact]); // Sectors collapse/expand - const searchParams = useSearchParams(); - const router = useRouter(); - const pathname = usePathname(); - const params = new URLSearchParams(searchParams); - const [expandedSectors, setExpandedSectors] = useState( - params - .get("s") - ?.split(";") - .map((s) => parseInt(s)) ?? [] + const [expandedSectors, setExpandedSectors] = useQueryState( + "sectors", + parseAsArrayOf(parseAsInteger).withDefault([]) ); - // const createSectorQuery = useCallback( - // (index: number) => { - // const params = new URLSearchParams(searchParams); - // let sectors = - // params - // .get("s") - // ?.split(";") - // .map((s) => parseInt(s)) ?? []; - // let anchor = ""; - // if (sectors.includes(index)) { - // sectors = sectors.filter((s) => s !== index); - // } else { - // sectors.push(index); - // sectors.sort(); - // anchor = `#s${index}`; - // } - - // if (sectors.length === 0) { - // params.delete("s"); - // } else { - // params.set("s", sectors.join(";")); - // } - - // setExpandedSectors(sectors); - - // // return decodeURIComponent(params.toString()); - // }, - // [searchParams] - // ); - - // const saveScrollPosition = useCallback(() => { - // localStorage.setItem("persistentScroll", window.scrollY.toString()); - // }, []); - const toggleSector = (index: number) => { let sectors = [...expandedSectors]; if (expandedSectors.includes(index)) { sectors = sectors.filter((s) => s !== index); } else { sectors.push(index); - // sectors.sort(); + sectors.sort(); } setExpandedSectors(sectors); - - // console.log(window.location); - // const url = new URL(window.location.href); - // url.searchParams.set("s", decodeURIComponent(sectors.join(";"))); - // history.pushState({}, "", url); - - // createSectorQuery(index); - // saveScrollPosition(); - // router.push(`${pathname}?${createSectorQuery(index)}`, { scroll: false }); }; - // useEffect(() => { - // const persistentScroll = localStorage.getItem("persistentScroll"); - // if (persistentScroll === null) return; - - // window.scrollTo({ top: Number(persistentScroll) }); - - // if (Number(persistentScroll) === window.scrollY) - // localStorage.removeItem("persistentScroll"); - // }, [searchParams]); + useLayoutEffect(() => { + const updatePosition = () => { + localStorage.setItem("persistentScroll", window.scrollY.toString()); + }; + const persistentScroll = localStorage.getItem("persistentScroll"); + if (persistentScroll) { + window.scrollTo({ top: Number(persistentScroll) }); + } + window.addEventListener("scroll", updatePosition); + updatePosition(); + return () => window.removeEventListener("scroll", updatePosition); + }, []); return ( diff --git a/src/app/[lang]/crag/[cragSlug]/gallery/components/image-list/image-list-element.tsx b/src/app/[lang]/crag/[cragSlug]/gallery/components/image-list/image-list-element.tsx index fb1b500..3a6b9a1 100644 --- a/src/app/[lang]/crag/[cragSlug]/gallery/components/image-list/image-list-element.tsx +++ b/src/app/[lang]/crag/[cragSlug]/gallery/components/image-list/image-list-element.tsx @@ -24,12 +24,7 @@ function ImageListElement({ image, baseUrl }: TImageListElementParams) { priority /> {title &&
{title}
} - {author && ( -
- - {author} -
- )} + {author &&
{author}
} ); } diff --git a/src/utils/route-helpers.ts b/src/utils/route-helpers.ts index c99fd3b..707dcc3 100644 --- a/src/utils/route-helpers.ts +++ b/src/utils/route-helpers.ts @@ -1,7 +1,7 @@ import { AppRouterInstance, NavigateOptions, -} from "next/dist/shared/lib/app-router-context"; +} from "next/dist/shared/lib/app-router-context.shared-runtime"; import { ReadonlyURLSearchParams } from "next/navigation"; import { NextRouter } from "next/router";