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