import * as React from "react"; import * as R from "ramda"; import { graphql, Link, navigate, PageProps } from "gatsby"; import { Helmet } from "react-helmet"; import debounce from "lodash.debounce"; // import { Picker, Item } from "@adobe/react-spectrum"; import MasonryGallery from "../components/MasonryGallery"; import KeywordsPicker from "../components/KeywordsPicker"; import { compareDates, getGalleryPageUrl, getHelmetSafeBodyStyle, getVibrantStyle, } from "../utils"; import Nav from "../components/Nav"; import { Item, Select } from "../components/Select"; import { Switch } from "../components/Switch"; import ColorPalette from "@spectrum-icons/workflow/ColorPalette"; import { ToggleButton } from "../components/ToggleButton"; const SORT_KEYS = { hue: ["fields", "imageMeta", "vibrantHue"], rating: ["fields", "imageMeta", "meta", "Rating"], // hue_debug: ["fields", "imageMeta", "dominantHue", 0], hue_debug: ["fields", "imageMeta", "dominantHue", "0"], date: ["fields", "imageMeta", "dateTaken"], datePublished: ["fields", "imageMeta", "datePublished"], } as const; export type GalleryImage = Queries.GalleryPageQueryQuery["all"]["nodes"][number]; function smartCompareDates( key: keyof typeof SORT_KEYS, left: GalleryImage, right: GalleryImage ) { let diff = compareDates(SORT_KEYS[key], left, right); if (diff !== 0) { return diff; } return compareDates(SORT_KEYS.date, left, right); } const GalleryPage = ({ data, location, }: PageProps) => { const hash = location.hash ? location.hash.replace("#", "") : ""; const params = new URLSearchParams(location.search); const filterKeyword = params.get("filter"); const sortKey = params.get("sort") ?? "rating"; const showDebug = Boolean(params.get("debug")?.length); const [showPalette, setShowPalette] = React.useState(false); const onKeywordPick = React.useCallback((newKeyword: string | null) => { if (newKeyword) { try { window.plausible("Filter Keyword", { props: { keyword: newKeyword }, }); } catch (e) { // do nothing } } }, []); const setSortKey = React.useCallback( (newSortKey: string) => { try { window.plausible("Sort Gallery", { props: { key: newSortKey }, }); } catch (e) { // do nothing } navigate( getGalleryPageUrl( { sortKey: newSortKey, keyword: filterKeyword, showDebug }, hash ) // { replace: true } ); }, [filterKeyword, hash, showDebug] ); const removeHash = React.useCallback(() => { if (!hash.length) { return; } navigate( getGalleryPageUrl({ sortKey, keyword: filterKeyword, showDebug }, ""), { replace: true } ); window.removeEventListener("scroll", removeHash); }, [hash, sortKey, filterKeyword, showDebug]); React.useEffect(() => { // window.addEventListener("scroll", removeHash); return () => { window.removeEventListener("scroll", removeHash); }; }, [removeHash]); React.useEffect(() => { // hacky but it works for now requestAnimationFrame(() => { // don't scroll into view if user got here with back button or if we just cleared it if (!hash.length) { return; } const el = document.getElementById(hash); if (!el) { console.log("⚠️failed to find hash"); return; } console.log("scrolling into view manually", el.offsetTop); el.scrollIntoView({ block: hash.startsWith("all") ? "start" : "center", behavior: "smooth", }); setTimeout(() => { window.addEventListener("scroll", removeHash); }, 100); }); }, [hash, removeHash]); const images: GalleryImage[] = React.useMemo(() => { const sort = sortKey === "date" || sortKey === "datePublished" ? R.sort((node1: typeof data["all"]["nodes"][number], node2) => smartCompareDates(sortKey, node1, node2) ) : R.sort( // @ts-ignore R.descend(R.path(SORT_KEYS[sortKey])) ); const filter = filterKeyword ? R.filter((image) => R.includes( filterKeyword, R.pathOr([], ["fields", "imageMeta", "meta", "Keywords"], image) ) ) : R.identity; try { const ret = R.pipe( // @ts-ignore sort, filter )(data.all.nodes) as any; return ret; } catch (e) { console.log("caught images!", e); return []; } }, [data, sortKey, filterKeyword]); const recents = React.useMemo(() => { return R.sort( (left, right) => smartCompareDates("datePublished", left, right), data.recents.nodes ); }, [data]); const [dbgTags, setDbgTags] = React.useState(false); const [dbgSortKey, setDbgSortKey] = React.useState(false); const [dbgName, setDbgName] = React.useState(false); const dataFn = React.useCallback( (image: GalleryImage): string[] | null => { if (!showDebug) { return null; } let data: string[] = []; if (dbgName) { data.push(image.base); } if (dbgSortKey) { switch (sortKey) { case "hue": case "rating": { data.push(R.pathOr("x", SORT_KEYS[sortKey], image)); break; } case "date": case "datePublished": { const date = R.pathOr(null, SORT_KEYS[sortKey], image); if (date) { data.push(new Date(date).toLocaleString()); } else { data.push("x"); } break; } } } if (dbgTags) { data.push(image.fields?.imageMeta?.meta?.Keywords?.join(",") ?? "x"); } return data; }, [showDebug, sortKey, dbgName, dbgSortKey, dbgTags] ); return ( <> {/* @ts-ignore */} Photo Gallery | Chuck Dries

Recently published

{sortKey !== "datePublished" && ( show more )}

All images

selected ? getGalleryPageUrl({ keyword: null, sortKey, showDebug }, hash) : getGalleryPageUrl({ keyword: val, sortKey, showDebug }, hash) } keywords={[ "Boyce Thompson Arboretum", "winter", "night", "coast", // "city", "landscape", "flowers", "product", // "waterfall", // "fireworks", // "panoramic", "Portland Japanese Garden", // "shoot the light", // "sunset", ]} onPick={onKeywordPick} value={filterKeyword} />
{showDebug && (
name sort key tags
)}
setShowPalette(val)} >
); }; export const query = graphql` query GalleryPageQuery { recents: allFile( filter: { sourceInstanceName: { eq: "gallery" } } sort: { fields: { imageMeta: { datePublished: DESC } } } limit: 10 ) { ...GalleryImageFile } all: allFile( filter: { sourceInstanceName: { eq: "gallery" } } sort: { fields: { imageMeta: { dateTaken: DESC } } } ) { ...GalleryImageFile } } fragment GalleryImageFile on FileConnection { nodes { base childImageSharp { fluid { aspectRatio } gatsbyImageData( layout: CONSTRAINED height: 550 placeholder: DOMINANT_COLOR ) } fields { imageMeta { vibrantHue dominantHue dateTaken datePublished meta { Keywords Rating ObjectName CreateDate ModifyDate } vibrant { ...VibrantColors } } } } } `; export default GalleryPage;