From 7cdc315578529fbadc4ca15d5db5e1beb3effb5d Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 11:26:30 +0000 Subject: [PATCH] feat(web): preserve scroll position and add query params to about and brand pages - Add resetScroll: false to navigate calls in about.tsx to preserve scroll position - Add query params handling to brand.tsx (type: visual/typography/color, id) - Add resetScroll: false to navigate calls in brand.tsx - Fix potential issue where invalid deep links now properly clear selection Co-Authored-By: john@hyprnote.com --- apps/web/src/routes/_view/about.tsx | 18 +++++-- apps/web/src/routes/_view/brand.tsx | 73 +++++++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 7 deletions(-) diff --git a/apps/web/src/routes/_view/about.tsx b/apps/web/src/routes/_view/about.tsx index c87f640b6..48b835587 100644 --- a/apps/web/src/routes/_view/about.tsx +++ b/apps/web/src/routes/_view/about.tsx @@ -163,11 +163,15 @@ function Component() { const founder = founders.find((f) => f.id === search.id); if (founder) { setSelectedItem({ type: "founder", data: founder }); + } else { + setSelectedItem(null); } } else if (search.type === "photo" && search.id) { const photo = teamPhotos.find((p) => p.id === search.id); if (photo) { setSelectedItem({ type: "photo", data: photo }); + } else { + setSelectedItem(null); } } else { setSelectedItem(null); @@ -177,13 +181,19 @@ function Component() { const handleSetSelectedItem = (item: SelectedItem | null) => { setSelectedItem(item); if (item === null) { - navigate({ search: {} }); + navigate({ search: {}, resetScroll: false }); } else if (item.type === "story") { - navigate({ search: { type: "story" } }); + navigate({ search: { type: "story" }, resetScroll: false }); } else if (item.type === "founder") { - navigate({ search: { type: "founder", id: item.data.id } }); + navigate({ + search: { type: "founder", id: item.data.id }, + resetScroll: false, + }); } else if (item.type === "photo") { - navigate({ search: { type: "photo", id: item.data.id } }); + navigate({ + search: { type: "photo", id: item.data.id }, + resetScroll: false, + }); } }; diff --git a/apps/web/src/routes/_view/brand.tsx b/apps/web/src/routes/_view/brand.tsx index a22a638a4..675d4569c 100644 --- a/apps/web/src/routes/_view/brand.tsx +++ b/apps/web/src/routes/_view/brand.tsx @@ -1,7 +1,7 @@ -import { createFileRoute } from "@tanstack/react-router"; +import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { Menu, X, XIcon } from "lucide-react"; import { AnimatePresence, motion } from "motion/react"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { ResizableHandle, @@ -13,8 +13,24 @@ import { cn } from "@hypr/utils"; import { MockWindow } from "@/components/mock-window"; +type BrandSearch = { + type?: "visual" | "typography" | "color"; + id?: string; +}; + export const Route = createFileRoute("/_view/brand")({ component: Component, + validateSearch: (search: Record): BrandSearch => { + return { + type: + search.type === "visual" || + search.type === "typography" || + search.type === "color" + ? search.type + : undefined, + id: typeof search.id === "string" ? search.id : undefined, + }; + }, head: () => ({ meta: [ { title: "Brand - Hyprnote Press Kit" }, @@ -120,8 +136,59 @@ type SelectedItem = | { type: "color"; data: (typeof COLORS)[0] }; function Component() { + const navigate = useNavigate({ from: Route.fullPath }); + const search = Route.useSearch(); const [selectedItem, setSelectedItem] = useState(null); + useEffect(() => { + if (search.type === "visual" && search.id) { + const asset = VISUAL_ASSETS.find((a) => a.id === search.id); + if (asset) { + setSelectedItem({ type: "visual", data: asset }); + } else { + setSelectedItem(null); + } + } else if (search.type === "typography" && search.id) { + const font = TYPOGRAPHY.find((f) => f.id === search.id); + if (font) { + setSelectedItem({ type: "typography", data: font }); + } else { + setSelectedItem(null); + } + } else if (search.type === "color" && search.id) { + const color = COLORS.find((c) => c.id === search.id); + if (color) { + setSelectedItem({ type: "color", data: color }); + } else { + setSelectedItem(null); + } + } else { + setSelectedItem(null); + } + }, [search.type, search.id]); + + const handleSetSelectedItem = (item: SelectedItem | null) => { + setSelectedItem(item); + if (item === null) { + navigate({ search: {}, resetScroll: false }); + } else if (item.type === "visual") { + navigate({ + search: { type: "visual", id: item.data.id }, + resetScroll: false, + }); + } else if (item.type === "typography") { + navigate({ + search: { type: "typography", id: item.data.id }, + resetScroll: false, + }); + } else if (item.type === "color") { + navigate({ + search: { type: "color", id: item.data.id }, + resetScroll: false, + }); + } + }; + return (