store sort and filterkeyword in urls and history state instead of localstorage
This commit is contained in:
parent
8ac3dd27dd
commit
4bfd662138
6
TODO.md
6
TODO.md
@ -1,7 +1,9 @@
|
|||||||
- [ ] Resume/Projects portfolio
|
- [ ] Resume/Projects portfolio
|
||||||
- [ ] Blog
|
- [ ] Blog
|
||||||
- [ ] tags
|
- [x] tags
|
||||||
- [ ] photo stories/collection pages
|
- [ ] photo stories/collection pages
|
||||||
- [ ] typescript (w/ graphql codegen)
|
- [ ] typescript (w/ graphql codegen)
|
||||||
|
- [ ] CMS
|
||||||
- [x] gallery sort and filter preserved in url (use-query or whatever?)
|
- [x] gallery sort and filter preserved in url (use-query or whatever?)
|
||||||
- [ ] nav next/prev buttons use saved gallery sort and filter
|
- [x] nav next/prev buttons use saved gallery sort and filter
|
||||||
|
- [ ] bug: "gallery" buttin in nav should reset filter and sort selections
|
||||||
|
@ -220,16 +220,16 @@ exports.createPages = async ({ graphql, actions, reporter }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
edges.forEach(({ node }, index) => {
|
edges.forEach(({ node }, index) => {
|
||||||
const nextImage =
|
// const nextImage =
|
||||||
index === edges.length - 1 ? null : edges[index + 1].node.base;
|
// index === edges.length - 1 ? null : edges[index + 1].node.base;
|
||||||
const prevImage = index === 0 ? null : edges[index - 1].node.base;
|
// const prevImage = index === 0 ? null : edges[index - 1].node.base;
|
||||||
const page = {
|
const page = {
|
||||||
path: `photogallery/${node.base}`,
|
path: `photogallery/${node.base}`,
|
||||||
component: galleryImageTemplate,
|
component: galleryImageTemplate,
|
||||||
context: {
|
context: {
|
||||||
imageFilename: node.base,
|
imageFilename: node.base,
|
||||||
nextImage,
|
// nextImage,
|
||||||
prevImage,
|
// prevImage,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
createPage(page);
|
createPage(page);
|
||||||
|
@ -21,6 +21,7 @@ import {
|
|||||||
getHelmetSafeBodyStyle,
|
getHelmetSafeBodyStyle,
|
||||||
hasName,
|
hasName,
|
||||||
getCanonicalSize,
|
getCanonicalSize,
|
||||||
|
getGalleryPageUrl,
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import MetadataItem from "./MetadataItem";
|
import MetadataItem from "./MetadataItem";
|
||||||
|
|
||||||
@ -35,33 +36,64 @@ const logKeyShortcut = (keyCode) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const GalleryImage = ({ data, pageContext }) => {
|
const GalleryImage = ({
|
||||||
|
data,
|
||||||
|
location: {
|
||||||
|
state: { sortedImageList, currentIndex, filterKeyword, sortKey },
|
||||||
|
},
|
||||||
|
}) => {
|
||||||
const image = data.file;
|
const image = data.file;
|
||||||
const ar = getAspectRatio(image);
|
const ar = getAspectRatio(image);
|
||||||
|
|
||||||
const [zoom, setZoom] = useState(false);
|
const [zoom, setZoom] = useState(false);
|
||||||
|
|
||||||
|
const nextIndex =
|
||||||
|
sortedImageList && currentIndex < sortedImageList.length
|
||||||
|
? currentIndex + 1
|
||||||
|
: null;
|
||||||
|
const prevIndex =
|
||||||
|
sortedImageList && currentIndex > 0 ? currentIndex - 1 : null;
|
||||||
|
|
||||||
|
const nextImage = sortedImageList && sortedImageList[nextIndex];
|
||||||
|
const prevImage = sortedImageList && sortedImageList[prevIndex];
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const keyListener = (e) => {
|
const keyListener = (e) => {
|
||||||
switch (e.code) {
|
switch (e.code) {
|
||||||
case "ArrowRight": {
|
case "ArrowRight": {
|
||||||
logKeyShortcut(e.code);
|
logKeyShortcut(e.code);
|
||||||
if (pageContext.nextImage) {
|
if (nextImage) {
|
||||||
navigate(`/photogallery/${pageContext.nextImage}/`);
|
navigate(`/photogallery/${nextImage}/`, {
|
||||||
|
state: {
|
||||||
|
currentIndex: currentIndex + 1,
|
||||||
|
sortedImageList,
|
||||||
|
filterKeyword,
|
||||||
|
sortKey,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case "ArrowLeft": {
|
case "ArrowLeft": {
|
||||||
logKeyShortcut(e.code);
|
logKeyShortcut(e.code);
|
||||||
if (pageContext.prevImage) {
|
if (prevImage) {
|
||||||
navigate(`/photogallery/${pageContext.prevImage}/`);
|
navigate(`/photogallery/${prevImage}/`, {
|
||||||
|
state: {
|
||||||
|
currentIndex: currentIndex - 1,
|
||||||
|
sortedImageList,
|
||||||
|
filterKeyword,
|
||||||
|
sortKey,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case "Escape":
|
case "Escape":
|
||||||
case "KeyG": {
|
case "KeyG": {
|
||||||
logKeyShortcut(e.code);
|
logKeyShortcut(e.code);
|
||||||
navigate(`/photogallery/#${image.base}`);
|
navigate(
|
||||||
|
getGalleryPageUrl({ keyword: filterKeyword, sortKey }, image.base)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -69,7 +101,15 @@ const GalleryImage = ({ data, pageContext }) => {
|
|||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener("keydown", keyListener);
|
document.removeEventListener("keydown", keyListener);
|
||||||
};
|
};
|
||||||
}, [pageContext, image.base]);
|
}, [
|
||||||
|
nextImage,
|
||||||
|
prevImage,
|
||||||
|
image.base,
|
||||||
|
currentIndex,
|
||||||
|
sortedImageList,
|
||||||
|
filterKeyword,
|
||||||
|
sortKey,
|
||||||
|
]);
|
||||||
|
|
||||||
const name = getName(image);
|
const name = getName(image);
|
||||||
const { meta, dateTaken: dt } = getMeta(image);
|
const { meta, dateTaken: dt } = getMeta(image);
|
||||||
@ -118,22 +158,37 @@ const GalleryImage = ({ data, pageContext }) => {
|
|||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
|
className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
|
||||||
to={`/photogallery/#${image.base}`}
|
to={getGalleryPageUrl(
|
||||||
|
{ keyword: filterKeyword, sortKey },
|
||||||
|
image.base
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
gallery <span className="bg-gray-300 text-black">esc</span>
|
gallery <span className="bg-gray-300 text-black">esc</span>
|
||||||
</Link>
|
</Link>
|
||||||
{pageContext.prevImage && (
|
{prevImage && (
|
||||||
<Link
|
<Link
|
||||||
className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
|
className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
|
||||||
to={`/photogallery/${pageContext.prevImage}/`}
|
state={{
|
||||||
|
currentIndex: currentIndex - 1,
|
||||||
|
sortedImageList,
|
||||||
|
filterKeyword,
|
||||||
|
sortKey,
|
||||||
|
}}
|
||||||
|
to={`/photogallery/${prevImage}/`}
|
||||||
>
|
>
|
||||||
previous <span className="bg-gray-300 text-black">⭠</span>
|
previous <span className="bg-gray-300 text-black">⭠</span>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
{pageContext.nextImage && (
|
{nextImage && (
|
||||||
<Link
|
<Link
|
||||||
className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
|
className="hover:underline text-vibrant-light hover:text-muted-light mx-1"
|
||||||
to={`/photogallery/${pageContext.nextImage}/`}
|
state={{
|
||||||
|
currentIndex: currentIndex + 1,
|
||||||
|
sortedImageList,
|
||||||
|
filterKeyword,
|
||||||
|
sortKey,
|
||||||
|
}}
|
||||||
|
to={`/photogallery/${nextImage}/`}
|
||||||
>
|
>
|
||||||
next <span className="bg-gray-300 text-black">⭢</span>
|
next <span className="bg-gray-300 text-black">⭢</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -13,11 +13,12 @@ const MasonryGallery = ({
|
|||||||
aspectsByBreakpoint: aspectTargetsByBreakpoint,
|
aspectsByBreakpoint: aspectTargetsByBreakpoint,
|
||||||
debugHue,
|
debugHue,
|
||||||
debugRating,
|
debugRating,
|
||||||
|
linkState,
|
||||||
}) => {
|
}) => {
|
||||||
const breakpoints = React.useMemo(
|
const breakpoints = React.useMemo(
|
||||||
() => R.pick(R.keys(aspectTargetsByBreakpoint), themeBreakpoints),
|
() => R.pick(R.keys(aspectTargetsByBreakpoint), themeBreakpoints),
|
||||||
[aspectTargetsByBreakpoint]
|
[aspectTargetsByBreakpoint]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { breakpoint } = useBreakpoint(breakpoints, "sm");
|
const { breakpoint } = useBreakpoint(breakpoints, "sm");
|
||||||
|
|
||||||
@ -65,81 +66,89 @@ const MasonryGallery = ({
|
|||||||
[aspectRatios, targetAspect]
|
[aspectRatios, targetAspect]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const sortedImageList = React.useMemo(
|
||||||
|
() => images.map((image) => image.base),
|
||||||
|
[images]
|
||||||
|
);
|
||||||
|
|
||||||
let cursor = 0;
|
let cursor = 0;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* {breakpoint} */}
|
<div
|
||||||
<div
|
className="w-full flex items-center flex-wrap"
|
||||||
className="w-full flex items-center flex-wrap"
|
style={{
|
||||||
style={{
|
position: "relative",
|
||||||
position: "relative",
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{images.map((image, i) => {
|
||||||
{images.map((image, i) => {
|
let currentRow = rows[cursor];
|
||||||
let currentRow = rows[cursor];
|
if (rows[i]) {
|
||||||
if (rows[i]) {
|
cursor = i;
|
||||||
cursor = i;
|
currentRow = rows[i];
|
||||||
currentRow = rows[i];
|
}
|
||||||
}
|
const rowAspectRatioSum = currentRow.aspect;
|
||||||
const rowAspectRatioSum = currentRow.aspect;
|
const ar = getAspectRatio(image);
|
||||||
const ar = getAspectRatio(image);
|
let width;
|
||||||
let width;
|
let height = `calc(100vw / ${rowAspectRatioSum} - 10px)`;
|
||||||
let height = `calc(100vw / ${rowAspectRatioSum} - 10px)`;
|
if (rowAspectRatioSum < targetAspect * 0.66) {
|
||||||
if (rowAspectRatioSum < targetAspect * 0.66) {
|
// incomplete row, render stuff at "ideal" sizes instead of filling width
|
||||||
// incomplete row, render stuff at "ideal" sizes instead of filling width
|
width = `calc(100vw / ${targetAspect / ar})`;
|
||||||
width = `calc(100vw / ${targetAspect / ar})`;
|
height = "unset";
|
||||||
height = "unset";
|
} else {
|
||||||
} else {
|
const widthNumber = ((ar / rowAspectRatioSum) * 100).toFixed(7);
|
||||||
const widthNumber = ((ar / rowAspectRatioSum) * 100).toFixed(7);
|
width = `${widthNumber}%`;
|
||||||
width = `${widthNumber}%`;
|
}
|
||||||
}
|
return (
|
||||||
return (
|
<Link
|
||||||
<Link
|
className={classNames(
|
||||||
className={classNames(
|
"border-4 overflow-hidden",
|
||||||
"border-4 overflow-hidden",
|
debugHue && "border-8"
|
||||||
debugHue && "border-8"
|
)}
|
||||||
)}
|
id={image.base}
|
||||||
id={image.base}
|
key={`${image.base}`}
|
||||||
key={`${image.base}`}
|
state={{
|
||||||
state={{ modal: true }}
|
...linkState,
|
||||||
style={{
|
sortedImageList,
|
||||||
height,
|
currentIndex: i,
|
||||||
width,
|
}}
|
||||||
// borderColor: `hsl(${image.fields.imageMeta.dominantHue}, 100%, 50%)`
|
style={{
|
||||||
// borderColor: `rgb(${image.fields.imageMeta.vibrant.Vibrant.join(',')})`
|
height,
|
||||||
borderColor: debugHue
|
width,
|
||||||
? `hsl(
|
// borderColor: `hsl(${image.fields.imageMeta.dominantHue}, 100%, 50%)`
|
||||||
|
// borderColor: `rgb(${image.fields.imageMeta.vibrant.Vibrant.join(',')})`
|
||||||
|
borderColor: debugHue
|
||||||
|
? `hsl(
|
||||||
${image.fields.imageMeta.dominantHue[0]},
|
${image.fields.imageMeta.dominantHue[0]},
|
||||||
${image.fields.imageMeta.dominantHue[1] * 100}%,
|
${image.fields.imageMeta.dominantHue[1] * 100}%,
|
||||||
${image.fields.imageMeta.dominantHue[2] * 100}%
|
${image.fields.imageMeta.dominantHue[2] * 100}%
|
||||||
)`
|
)`
|
||||||
: "black",
|
: "black",
|
||||||
}}
|
}}
|
||||||
to={`/photogallery/${image.base}`}
|
to={`/photogallery/${image.base}`}
|
||||||
>
|
>
|
||||||
{debugHue && (
|
{debugHue && (
|
||||||
<span className="text-white z-20 absolute bg-black">
|
<span className="text-white z-20 absolute bg-black">
|
||||||
hsl(
|
hsl(
|
||||||
{image.fields.imageMeta.dominantHue[0]},{" "}
|
{image.fields.imageMeta.dominantHue[0]},{" "}
|
||||||
{(image.fields.imageMeta.dominantHue[1] * 100).toFixed(2)}%,{" "}
|
{(image.fields.imageMeta.dominantHue[1] * 100).toFixed(2)}%,{" "}
|
||||||
{(image.fields.imageMeta.dominantHue[2] * 100).toFixed(2)}% )
|
{(image.fields.imageMeta.dominantHue[2] * 100).toFixed(2)}% )
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{debugRating && (
|
{debugRating && (
|
||||||
<span className="text-white z-20 absolute bg-black">
|
<span className="text-white z-20 absolute bg-black">
|
||||||
rating: {image.fields.imageMeta.meta.Rating}
|
rating: {image.fields.imageMeta.meta.Rating}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<GatsbyImage
|
<GatsbyImage
|
||||||
alt={getName(image)}
|
alt={getName(image)}
|
||||||
className="w-full h-full"
|
className="w-full h-full"
|
||||||
image={getImage(image)}
|
image={getImage(image)}
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -7,6 +7,7 @@ import { Picker, Item } from "@adobe/react-spectrum";
|
|||||||
|
|
||||||
import MasonryGallery from "../../components/MasonryGallery";
|
import MasonryGallery from "../../components/MasonryGallery";
|
||||||
import KeywordsPicker from "../../components/KeywordsPicker";
|
import KeywordsPicker from "../../components/KeywordsPicker";
|
||||||
|
import { getGalleryPageUrl } from "../../utils";
|
||||||
|
|
||||||
const SORT_KEYS = {
|
const SORT_KEYS = {
|
||||||
hue: ["fields", "imageMeta", "vibrantHue"],
|
hue: ["fields", "imageMeta", "vibrantHue"],
|
||||||
@ -15,27 +16,10 @@ const SORT_KEYS = {
|
|||||||
date: [],
|
date: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const getUrl = ({ keyword, sortKey }) => {
|
|
||||||
const url = new URL(window.location.toString());
|
|
||||||
if (keyword !== undefined) {
|
|
||||||
if (keyword === null) {
|
|
||||||
url.searchParams.delete("filter");
|
|
||||||
} else {
|
|
||||||
url.searchParams.set("filter", keyword);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sortKey !== undefined) {
|
|
||||||
if (sortKey === "rating") {
|
|
||||||
url.searchParams.delete("sort");
|
|
||||||
} else {
|
|
||||||
url.searchParams.set("sort", sortKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return url.toString();
|
|
||||||
};
|
|
||||||
|
|
||||||
const GalleryPage = ({ data }) => {
|
const GalleryPage = ({ data }) => {
|
||||||
const [keyword, _setKeyword] = React.useState(null);
|
const hash = window.location.hash.replace("#", "");
|
||||||
|
|
||||||
|
const [filterKeyword, _setKeyword] = React.useState(null);
|
||||||
const [sortKey, _setSortKey] = React.useState("rating");
|
const [sortKey, _setSortKey] = React.useState("rating");
|
||||||
const showDebug =
|
const showDebug =
|
||||||
typeof window !== "undefined" &&
|
typeof window !== "undefined" &&
|
||||||
@ -51,10 +35,13 @@ const GalleryPage = ({ data }) => {
|
|||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
_setKeyword(newKeyword);
|
_setKeyword(newKeyword);
|
||||||
localStorage?.setItem("photogallery.keyword", newKeyword);
|
window.history.replaceState(
|
||||||
window.history.replaceState(null, "", getUrl({ keyword: newKeyword }));
|
null,
|
||||||
|
"",
|
||||||
|
getGalleryPageUrl({ keyword: newKeyword, sortKey }, hash)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
[_setKeyword]
|
[_setKeyword, sortKey, hash]
|
||||||
);
|
);
|
||||||
|
|
||||||
const setSortKey = React.useCallback(
|
const setSortKey = React.useCallback(
|
||||||
@ -67,48 +54,47 @@ const GalleryPage = ({ data }) => {
|
|||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
_setSortKey(newSortKey);
|
_setSortKey(newSortKey);
|
||||||
localStorage?.setItem("photogallery.sortkey2", newSortKey);
|
window.history.replaceState(
|
||||||
window.history.replaceState(null, "", getUrl({ sortKey: newSortKey }));
|
null,
|
||||||
|
"",
|
||||||
|
getGalleryPageUrl({ sortKey: newSortKey, keyword: filterKeyword }, hash)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
[_setSortKey]
|
[_setSortKey, filterKeyword, hash]
|
||||||
);
|
);
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const url = new URL(window.location.toString());
|
|
||||||
|
|
||||||
const sortKeyFromUrl = url.searchParams.get("sort");
|
|
||||||
const sortKeyFromStorage = localStorage.getItem("photogallery.sortkey2");
|
|
||||||
if (sortKeyFromUrl || sortKeyFromStorage) {
|
|
||||||
setSortKey(sortKeyFromUrl || sortKeyFromStorage);
|
|
||||||
}
|
|
||||||
|
|
||||||
const filterKeyFromUrl = url.searchParams.get("filter");
|
|
||||||
const filterKeyFromStorage = localStorage.getItem("photogallery.keyword");
|
|
||||||
if (filterKeyFromUrl || filterKeyFromStorage !== "null") {
|
|
||||||
setKeyword(filterKeyFromUrl || filterKeyFromStorage);
|
|
||||||
}
|
|
||||||
}, [setSortKey, setKeyword]);
|
|
||||||
|
|
||||||
const scrollIntoView = React.useCallback(() => {
|
const scrollIntoView = React.useCallback(() => {
|
||||||
if (!window.location.hash) {
|
if (!hash) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const el = document.getElementById(window.location.hash.split("#")[1]);
|
const el = document.getElementById(hash);
|
||||||
if (!el) {
|
if (!el) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
el.scrollIntoView({
|
el.scrollIntoView({
|
||||||
block: "center",
|
block: "center",
|
||||||
});
|
});
|
||||||
}, []);
|
}, [hash]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
const url = new URL(window.location.toString());
|
||||||
|
|
||||||
|
const sortKeyFromUrl = url.searchParams.get("sort");
|
||||||
|
if (sortKeyFromUrl) {
|
||||||
|
_setSortKey(sortKeyFromUrl, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterKeyFromUrl = url.searchParams.get("filter");
|
||||||
|
if (filterKeyFromUrl) {
|
||||||
|
_setKeyword(filterKeyFromUrl, false);
|
||||||
|
}
|
||||||
|
|
||||||
// hacky but it works for now
|
// hacky but it works for now
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// don't scroll into view if user got here with back button
|
// don't scroll into view if user got here with back button
|
||||||
scrollIntoView();
|
scrollIntoView();
|
||||||
}, 100);
|
}, 100);
|
||||||
}, [scrollIntoView]);
|
}, [setSortKey, setKeyword, scrollIntoView]);
|
||||||
|
|
||||||
const images = React.useMemo(
|
const images = React.useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -124,16 +110,16 @@ const GalleryPage = ({ data }) => {
|
|||||||
return -1 * (date1.getTime() - date2.getTime());
|
return -1 * (date1.getTime() - date2.getTime());
|
||||||
})
|
})
|
||||||
: R.sort(R.descend(R.path(SORT_KEYS[sortKey]))),
|
: R.sort(R.descend(R.path(SORT_KEYS[sortKey]))),
|
||||||
keyword
|
filterKeyword
|
||||||
? R.filter((image) =>
|
? R.filter((image) =>
|
||||||
R.includes(
|
R.includes(
|
||||||
keyword,
|
filterKeyword,
|
||||||
R.path(["fields", "imageMeta", "meta", "Keywords"], image)
|
R.path(["fields", "imageMeta", "meta", "Keywords"], image)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
: R.identity
|
: R.identity
|
||||||
)(data.allFile.nodes),
|
)(data.allFile.nodes),
|
||||||
[data, sortKey, keyword]
|
[data, sortKey, filterKeyword]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -183,7 +169,7 @@ const GalleryPage = ({ data }) => {
|
|||||||
// "sunset",
|
// "sunset",
|
||||||
]}
|
]}
|
||||||
onChange={setKeyword}
|
onChange={setKeyword}
|
||||||
value={keyword}
|
value={filterKeyword}
|
||||||
/>
|
/>
|
||||||
<div className="m-2">
|
<div className="m-2">
|
||||||
<Picker
|
<Picker
|
||||||
@ -211,6 +197,10 @@ const GalleryPage = ({ data }) => {
|
|||||||
debugHue={sortKey === "hue_debug"}
|
debugHue={sortKey === "hue_debug"}
|
||||||
debugRating={sortKey === "rating" && showDebug}
|
debugRating={sortKey === "rating" && showDebug}
|
||||||
images={images}
|
images={images}
|
||||||
|
linkState={{
|
||||||
|
sortKey,
|
||||||
|
filterKeyword,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
25
src/utils.js
25
src/utils.js
@ -79,3 +79,28 @@ export const getShutterFractionFromExposureTime = (exposureTime) => {
|
|||||||
}
|
}
|
||||||
return `${numerator}/${denominator}`;
|
return `${numerator}/${denominator}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getGalleryPageUrl = (
|
||||||
|
{ keyword, sortKey },
|
||||||
|
hash
|
||||||
|
) => {
|
||||||
|
const url = new URL(`${window.location.origin}/photogallery/`);
|
||||||
|
if (keyword !== undefined) {
|
||||||
|
if (keyword === null) {
|
||||||
|
url.searchParams.delete("filter");
|
||||||
|
} else {
|
||||||
|
url.searchParams.set("filter", keyword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sortKey !== undefined) {
|
||||||
|
if (sortKey === "rating") {
|
||||||
|
url.searchParams.delete("sort");
|
||||||
|
} else {
|
||||||
|
url.searchParams.set("sort", sortKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hash) {
|
||||||
|
url.hash = hash;
|
||||||
|
}
|
||||||
|
return url.href.toString().replace(url.origin, '');
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user