an appview-less Bluesky client using Constellation and PDS Queries reddwarf.app
frontend spa bluesky reddwarf microcosm

better live update status of indexing

rimar1337 f922e15e 1414d177

Changed files
+86 -21
src
+48 -20
src/routes/search.tsx
··· 2 2 import { useQueryClient } from "@tanstack/react-query"; 3 3 import { createFileRoute, useSearch } from "@tanstack/react-router"; 4 4 import { useAtom } from "jotai"; 5 - import { useMemo } from "react"; 5 + import { useEffect,useMemo } from "react"; 6 6 7 7 import { Header } from "~/components/Header"; 8 8 import { Import } from "~/components/Import"; ··· 21 21 } from "~/utils/useQuery"; 22 22 23 23 import { renderSnack } from "./__root"; 24 + import { SliderPrimitive } from "./settings"; 24 25 25 26 export const Route = createFileRoute("/search")({ 26 27 component: Search, ··· 32 33 const { data: identity } = useQueryIdentity(agent?.did); 33 34 const [lycandomain] = useAtom(lycanURLAtom); 34 35 const lycanExists = lycandomain !== ""; 35 - const { data: lycanstatusdata } = useQueryLycanStatus(); 36 + const { data: lycanstatusdata, refetch } = useQueryLycanStatus(); 36 37 const lycanIndexed = lycanstatusdata?.status === "finished" || false; 38 + const lycanIndexing = lycanstatusdata?.status === "in_progress" || false; 39 + const lycanIndexingProgress = lycanIndexing 40 + ? lycanstatusdata?.progress 41 + : undefined; 42 + 37 43 const authed = status === "signedIn"; 38 44 39 45 const lycanReady = lycanExists && lycanIndexed && authed; 40 46 41 47 const { q }: { q: string } = useSearch({ from: "/search" }); 42 48 43 - //const lycanIndexed = useQuery(); 49 + // auto-refetch Lycan status until ready 50 + useEffect(() => { 51 + if (!lycanExists || !authed) return; 52 + if (lycanReady) return; 53 + 54 + const interval = setInterval(() => { 55 + refetch(); 56 + }, 3000); 57 + 58 + return () => clearInterval(interval); 59 + }, [lycanExists, authed, lycanReady, refetch]); 44 60 45 61 const maintext = !lycanExists 46 62 ? "Sorry we dont have search. But instead, you can load some of these types of content into Red Dwarf:" ··· 64 80 constructLycanRequestIndexQuery(opts) 65 81 ); 66 82 if ( 67 - response?.message !== "Import has already started" || 68 - response?.message !== "Import has already started" 83 + response?.message !== "Import has already started" && 84 + response?.message !== "Import has been scheduled" 69 85 ) { 70 86 renderSnack({ 71 87 title: "Registration failed!", ··· 76 92 title: "Succesfully sent registration request!", 77 93 description: "Please wait for the server to index your account", 78 94 }); 95 + refetch(); 79 96 } 80 97 } catch { 81 98 renderSnack({ ··· 127 144 </p> 128 145 129 146 {lycanExists && authed && !lycanReady ? ( 130 - <div className="mt-4 mx-auto"> 131 - <button 132 - onClick={() => 133 - index({ 134 - agent: agent || undefined, 135 - isAuthed: status === "signedIn", 136 - pdsUrl: identity?.pds, 137 - feedServiceDid: "did:web:" + lycandomain, 138 - }) 139 - } 140 - className="px-6 py-2 h-12 rounded-full bg-gray-100 dark:bg-gray-800 147 + !lycanIndexing ? ( 148 + <div className="mt-4 mx-auto"> 149 + <button 150 + onClick={() => 151 + index({ 152 + agent: agent || undefined, 153 + isAuthed: status === "signedIn", 154 + pdsUrl: identity?.pds, 155 + feedServiceDid: "did:web:" + lycandomain, 156 + }) 157 + } 158 + className="px-6 py-2 h-12 rounded-full bg-gray-100 dark:bg-gray-800 141 159 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 transition" 142 - > 143 - Index my Account 144 - </button> 145 - </div> 160 + > 161 + Index my Account 162 + </button> 163 + </div> 164 + ) : ( 165 + <div className="mt-4 gap-2 flex flex-col"> 166 + <span>indexing...</span> 167 + <SliderPrimitive 168 + value={lycanIndexingProgress || 0} 169 + min={0} 170 + max={1} 171 + /> 172 + </div> 173 + ) 146 174 ) : ( 147 175 <></> 148 176 )}
+33
src/routes/settings.tsx
··· 325 325 </Slider.Root> 326 326 ); 327 327 }; 328 + 329 + 330 + interface SliderPProps { 331 + value: number; 332 + min?: number; 333 + max?: number; 334 + step?: number; 335 + } 336 + 337 + 338 + export const SliderPrimitive: React.FC<SliderPProps> = ({ 339 + value, 340 + min = 0, 341 + max = 100, 342 + step = 1, 343 + }) => { 344 + 345 + return ( 346 + <Slider.Root 347 + className="relative flex items-center w-full h-4" 348 + value={[value]} 349 + min={min} 350 + max={max} 351 + step={step} 352 + onValueChange={(v: number[]) => {}} 353 + > 354 + <Slider.Track className="relative flex-grow h-4 bg-gray-300 dark:bg-gray-700 rounded-full"> 355 + <Slider.Range className="absolute h-full bg-gray-500 dark:bg-gray-400 rounded-l-full rounded-r-none" /> 356 + </Slider.Track> 357 + <Slider.Thumb className=" hidden shadow-[0_0_0_8px_var(--color-white)] dark:shadow-[0_0_0_8px_var(--color-gray-950)] block w-[3px] h-12 bg-gray-500 dark:bg-gray-400 rounded-md focus:outline-none" /> 358 + </Slider.Root> 359 + ); 360 + };
+5 -1
src/utils/useQuery.ts
··· 806 806 [key: string]: unknown; 807 807 error?: "MethodNotImplemented"; 808 808 message?: "Method Not Implemented"; 809 - status?: "finished"; 809 + status?: "finished" | "in_progress"; 810 + position?: string, 811 + progress?: number, 812 + 810 813 }; 811 814 815 + //{"status":"in_progress","position":"2025-08-30T06:53:18Z","progress":0.0878319661441268} 812 816 type importtype = { 813 817 message?: "Import has already started" | "Import has been scheduled" 814 818 }