an appview-less Bluesky client using Constellation and PDS Queries reddwarf.app
frontend spa bluesky reddwarf microcosm
1import "~/styles/app.css"; 2 3import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister"; 4import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 5import { persistQueryClient } from "@tanstack/react-query-persist-client"; 6import { createRouter, RouterProvider } from "@tanstack/react-router"; 7import { useSetAtom } from "jotai"; 8import { useEffect } from "react"; 9//import { StrictMode } from "react"; 10import ReactDOM from "react-dom/client"; 11 12import reportWebVitals from "./reportWebVitals.ts"; 13// Import the generated route tree 14import { routeTree } from "./routeTree.gen"; 15import { isAtTopAtom } from "./utils/atoms.ts"; 16 17const queryClient = new QueryClient({ 18 defaultOptions: { 19 queries: { 20 gcTime: 1000 * 60 * 60 * 24 * 24, // 24 days 21 }, 22 }, 23}); 24const localStoragePersister = createSyncStoragePersister({ 25 storage: window.localStorage, 26}); 27 28persistQueryClient({ 29 queryClient, 30 persister: localStoragePersister, 31}); 32 33// Create a new router instance 34const router = createRouter({ 35 routeTree, 36 context: { queryClient }, 37 defaultPreload: "intent", 38 scrollRestoration: true, 39 defaultStructuralSharing: true, 40 defaultPreloadStaleTime: 0, 41}); 42 43// Register the router instance for type safety 44declare module "@tanstack/react-router" { 45 interface Register { 46 router: typeof router; 47 } 48} 49 50// Render the app 51const rootElement = document.getElementById("app"); 52if (rootElement && !rootElement.innerHTML) { 53 const root = ReactDOM.createRoot(rootElement); 54 root.render( 55 // double queries annoys me 56 // <StrictMode> 57 <QueryClientProvider client={queryClient}> 58 <ScrollTopWatcher /> 59 <RouterProvider router={router} /> 60 </QueryClientProvider> 61 // </StrictMode> 62 ); 63} 64 65// If you want to start measuring performance in your app, pass a function 66// to log results (for example: reportWebVitals(// /*mass comment*/ console.log)) 67// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 68reportWebVitals(); 69 70export default function ScrollTopWatcher() { 71 const setIsAtTop = useSetAtom(isAtTopAtom); 72 useEffect(() => { 73 const meta = document.querySelector('meta[name="theme-color"]'); 74 let lastAtTop = window.scrollY === 0; 75 let timeoutId: number | undefined; 76 77 const setVars = (atTop: boolean) => { 78 const root = document.documentElement; 79 root.style.setProperty("--is-top", atTop ? "1" : "0"); 80 81 const bg = getComputedStyle(root).getPropertyValue("--header-bg").trim(); 82 if (meta && bg) meta.setAttribute("content", bg); 83 setIsAtTop(atTop); 84 }; 85 86 const check = () => { 87 const atTop = window.scrollY === 0; 88 if (atTop !== lastAtTop) { 89 lastAtTop = atTop; 90 setVars(atTop); 91 } 92 }; 93 94 const handleScroll = () => { 95 if (timeoutId) clearTimeout(timeoutId); 96 timeoutId = window.setTimeout(check, 2); 97 }; 98 99 // initialize 100 setVars(lastAtTop); 101 window.addEventListener("scroll", handleScroll, { passive: true }); 102 103 return () => { 104 window.removeEventListener("scroll", handleScroll); 105 if (timeoutId) clearTimeout(timeoutId); 106 }; 107 }, []); 108 109 return null; 110}