an independent Bluesky client using Constellation, PDS Queries, and other services reddwarf.app
frontend spa bluesky reddwarf microcosm client app

attampt at fixing av via fast bypasses

+253 -57
+10 -6
src/providers/PollMutationQueueProvider.tsx
··· 295 return context; 296 } 297 298 - function usePollSelfVotes(pollUri: string) { 299 const { agent } = useAuth(); 300 const agentDid = agent?.did; 301 302 const { uris: userVotesA } = useGetOneToOneState( 303 - agentDid 304 ? { 305 target: pollUri, 306 user: agentDid, 307 collection: "app.reddwarf.poll.vote.a", 308 path: ".subject.uri", 309 } 310 : undefined, 311 ); 312 const { uris: userVotesB } = useGetOneToOneState( 313 - agentDid 314 ? { 315 target: pollUri, 316 user: agentDid, 317 collection: "app.reddwarf.poll.vote.b", 318 path: ".subject.uri", 319 } 320 : undefined, 321 ); 322 const { uris: userVotesC } = useGetOneToOneState( 323 - agentDid 324 ? { 325 target: pollUri, 326 user: agentDid, 327 collection: "app.reddwarf.poll.vote.c", 328 path: ".subject.uri", 329 } 330 : undefined, 331 ); 332 const { uris: userVotesD } = useGetOneToOneState( 333 - agentDid 334 ? { 335 target: pollUri, 336 user: agentDid, 337 collection: "app.reddwarf.poll.vote.d", 338 path: ".subject.uri", 339 } 340 : undefined, 341 ); ··· 361 const myDid = agent?.did; 362 363 const { castVoteRaw, getLocalVotes } = usePollMutationQueue(); 364 - const serverUserVotes = usePollSelfVotes(pollUri); // Our own votes from server 365 const localVotes = getLocalVotes(pollUri); // Pending local actions 366 367 // 1. FETCHING - Move the logic here
··· 295 return context; 296 } 297 298 + function usePollSelfVotes(pollUri: string, enabled?: boolean) { 299 const { agent } = useAuth(); 300 const agentDid = agent?.did; 301 302 const { uris: userVotesA } = useGetOneToOneState( 303 + agentDid && enabled 304 ? { 305 target: pollUri, 306 user: agentDid, 307 collection: "app.reddwarf.poll.vote.a", 308 path: ".subject.uri", 309 + enabled: enabled 310 } 311 : undefined, 312 ); 313 const { uris: userVotesB } = useGetOneToOneState( 314 + agentDid && enabled 315 ? { 316 target: pollUri, 317 user: agentDid, 318 collection: "app.reddwarf.poll.vote.b", 319 path: ".subject.uri", 320 + enabled: enabled 321 } 322 : undefined, 323 ); 324 const { uris: userVotesC } = useGetOneToOneState( 325 + agentDid && enabled 326 ? { 327 target: pollUri, 328 user: agentDid, 329 collection: "app.reddwarf.poll.vote.c", 330 path: ".subject.uri", 331 + enabled: enabled 332 } 333 : undefined, 334 ); 335 const { uris: userVotesD } = useGetOneToOneState( 336 + agentDid && enabled 337 ? { 338 target: pollUri, 339 user: agentDid, 340 collection: "app.reddwarf.poll.vote.d", 341 path: ".subject.uri", 342 + enabled: enabled 343 } 344 : undefined, 345 ); ··· 365 const myDid = agent?.did; 366 367 const { castVoteRaw, getLocalVotes } = usePollMutationQueue(); 368 + const serverUserVotes = usePollSelfVotes(pollUri, enabled); // Our own votes from server 369 const localVotes = getLocalVotes(pollUri); // Pending local actions 370 371 // 1. FETCHING - Move the logic here
+88 -27
src/routes/about.tsx
··· 1 import { createFileRoute } from '@tanstack/react-router' 2 3 import { FORCED_LABELER_DIDS, HOST_ABOUT_MARKDOWN, HOST_ADMIN, HOST_DESCRIPTION, HOST_HERO, HOST_LABELMERGE, HOST_SIGNUP_PDS } from '~/../policy'; 4 import { Header } from '~/components/Header'; ··· 173 // endorsed feeds (should be shown in the explore tab too in lieu of feed discovery) 174 // - [ ] HOST_UNAUTHED_DEFAULT_FEEDS 175 // endorsed PDS 176 - // - [ ] HOST_SIGNUP_PDS 177 // todo move the other default services into policy.ts 178 // todo re- sort policy.ts according to this component 179 // also the default services used like microcosm stuff and lycan and maybe the reliance of an appview for search or some other hting ··· 181 // default general host moderation policies 182 // todo: layerd moderataion later pls thanks 183 // show the labelmerge insstance responsible 184 - // - [ ] HOST_LABELMERGE 185 // show both the whitelisted source and labeler dids in the same spot. 186 // like on hover / click it opens a dialog / popover to show what authority the labeler has 187 // - [x] FORCED_LABELER_DIDS ··· 197 return ( 198 <> 199 {/* settings heading or about heading? */} 200 <Heading3 title="Instance Defaults" /> 201 - <div className="grid grid-cols-2 gap-x-2 gap-y-2 text-sm text-gray-700 dark:text-gray-300 mr-auto ml-2"> 202 - <span className="font-medium">PDS (User Account Storage):</span> 203 - <span className={HOST_SIGNUP_PDS ? "" : "italic"}>{HOST_SIGNUP_PDS || "not set"}</span> 204 - 205 - <span className="font-medium">Labelmerge (Label Cache):</span> 206 - <span>{HOST_LABELMERGE || "not set"}</span> 207 - 208 - <span className="font-medium">Constellation (Backlink Index):</span> 209 - <span>{defaultconstellationURL || "not set"}</span> 210 - 211 - <span className="font-medium">Slingshot (Record Cache):</span> 212 - <span>{defaultslingshotURL || "not set"}</span> 213 - 214 - <span className="font-medium">Image Provider (CDN):</span> 215 - <span>{defaultImgCDN || "not set"}</span> 216 - 217 - <span className="font-medium">Video Provider (CDN):</span> 218 - <span>{defaultVideoCDN || "not set"}</span> 219 - 220 - <span className="font-medium">Lycan (Personal Search):</span> 221 - <span className={defaultLycanURL ? "" : "italic"}>{defaultLycanURL || "not set"}</span> 222 - 223 - <span className="font-medium">AppView (Bluesky Index):</span> 224 - <span className={defaultAppviewURL? "" : "italic"}>{defaultAppviewURL || "not set"}</span> 225 - </div> 226 {/* {hostmandate && (<Heading2 title="Host-Mandated Labelers" />)} */} 227 <Heading3 title="General Moderation" /> 228 {hostmandate && (<Heading4 title="Host-Mandated Labelers" />)} ··· 240 <div className='h-[300px] w-auto' /> 241 242 </> 243 ) 244 }
··· 1 import { createFileRoute } from '@tanstack/react-router' 2 + import React from 'react'; 3 4 import { FORCED_LABELER_DIDS, HOST_ABOUT_MARKDOWN, HOST_ADMIN, HOST_DESCRIPTION, HOST_HERO, HOST_LABELMERGE, HOST_SIGNUP_PDS } from '~/../policy'; 5 import { Header } from '~/components/Header'; ··· 174 // endorsed feeds (should be shown in the explore tab too in lieu of feed discovery) 175 // - [ ] HOST_UNAUTHED_DEFAULT_FEEDS 176 // endorsed PDS 177 + // - [x] HOST_SIGNUP_PDS 178 // todo move the other default services into policy.ts 179 // todo re- sort policy.ts according to this component 180 // also the default services used like microcosm stuff and lycan and maybe the reliance of an appview for search or some other hting ··· 182 // default general host moderation policies 183 // todo: layerd moderataion later pls thanks 184 // show the labelmerge insstance responsible 185 + // - [x] HOST_LABELMERGE 186 // show both the whitelisted source and labeler dids in the same spot. 187 // like on hover / click it opens a dialog / popover to show what authority the labeler has 188 // - [x] FORCED_LABELER_DIDS ··· 198 return ( 199 <> 200 {/* settings heading or about heading? */} 201 + <Heading3 title="Instance Configuration" /> 202 + <KeyValueGrid 203 + items={[ 204 + { 205 + label: "PDS Signups (Account Storage):", 206 + value: HOST_SIGNUP_PDS || "", 207 + }, 208 + { 209 + label: "Labelmerge (Label Cache):", 210 + value: HOST_LABELMERGE, 211 + }, 212 + ]} 213 + /> 214 <Heading3 title="Instance Defaults" /> 215 + <KeyValueGrid 216 + items={[ 217 + { 218 + label: "Constellation (Backlink Index):", 219 + value: defaultconstellationURL, 220 + //italicIfEmpty: true, 221 + }, 222 + { 223 + label: "Slingshot (Record Cache):", 224 + value: defaultslingshotURL, 225 + }, 226 + { 227 + label: "Image Provider (CDN):", 228 + value: defaultImgCDN, 229 + }, 230 + { 231 + label: "Video Provider (CDN):", 232 + value: defaultVideoCDN, 233 + }, 234 + { 235 + label: "Lycan (Personal Search):", 236 + value: defaultLycanURL, 237 + }, 238 + { 239 + label: "AppView (Bluesky Index):", 240 + value: defaultAppviewURL, 241 + }, 242 + ]} 243 + /> 244 {/* {hostmandate && (<Heading2 title="Host-Mandated Labelers" />)} */} 245 <Heading3 title="General Moderation" /> 246 {hostmandate && (<Heading4 title="Host-Mandated Labelers" />)} ··· 258 <div className='h-[300px] w-auto' /> 259 260 </> 261 + ) 262 + } 263 + 264 + type KeyValueItem = { 265 + label: string 266 + value?: string | null 267 + //italicIfEmpty?: boolean 268 + } 269 + 270 + interface KeyValueGridProps { 271 + items: KeyValueItem[] 272 + className?: string 273 + } 274 + 275 + export function KeyValueGrid({ items, className = "" }: KeyValueGridProps) { 276 + return ( 277 + <div 278 + className={`grid grid-cols-2 gap-x-2 gap-y-2 text-sm mr-auto ml-2 ${className}`} 279 + > 280 + {items.map((item, i) => { 281 + const isEmpty = !item.value 282 + 283 + return ( 284 + <React.Fragment key={i}> 285 + {/* Label */} 286 + <span className="font-medium text-gray-500 dark:text-gray-400"> 287 + {item.label} 288 + </span> 289 + 290 + {/* Value */} 291 + <span 292 + className={ 293 + isEmpty 294 + ? "text-gray-400 dark:text-gray-500 italic" 295 + : "text-gray-600 dark:text-gray-300" 296 + } 297 + > 298 + {item.value || "not set"} 299 + </span> 300 + </React.Fragment> 301 + ) 302 + })} 303 + </div> 304 ) 305 }
+77 -20
src/routes/profile.$did/post.$rkey.tsx
··· 1 import { AtUri } from "@atproto/api"; 2 - import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query"; 3 import { createFileRoute, Outlet, useMatchRoute } from "@tanstack/react-router"; 4 - import { useAtom } from "jotai"; 5 import React, { useLayoutEffect } from "react"; 6 7 import { Header } from "~/components/Header"; 8 import { UniversalPostRendererATURILoader } from "~/components/UniversalPostRenderer"; 9 - import { constellationURLAtom, slingshotURLAtom } from "~/utils/atoms"; 10 //import { usePersistentStore } from '~/providers/PersistentStoreProvider'; 11 import { 12 constructPostQuery, 13 type linksAllResponse, 14 type linksRecordsResponse, 15 useQueryConstellation, 16 - useQueryIdentity, 17 useQueryPost, 18 yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks, 19 } from "~/utils/useQuery"; ··· 189 // }; 190 // }, [atUri]); 191 192 const { 193 data: identity, 194 isLoading: isIdentityLoading, 195 error: identityError, 196 - } = useQueryIdentity(showMainPostRoute ? did : undefined); 197 198 const resolvedDid = did.startsWith("did:") ? did : identity?.did; 199 ··· 207 208 const { data: mainPost } = useQueryPost(showMainPostRoute ? atUri : undefined); 209 210 - console.log("atUri",atUri) 211 - 212 const opdid = React.useMemo( 213 () => 214 atUri ··· 218 ); 219 220 // @ts-expect-error i hate overloads 221 - const { data: links } = useQueryConstellation(atUri&&showMainPostRoute?{ 222 method: "/links/all", 223 target: atUri, 224 } : { 225 method: "undefined", 226 target: "" 227 - })as { data: linksAllResponse | undefined }; 228 229 //const [likes, setLikes] = React.useState<number | null>(null); 230 //const [reposts, setReposts] = React.useState<number | null>(null); ··· 245 setReplyCount( 246 links 247 ? links?.links?.["app.bsky.feed.post"]?.[".reply.parent.uri"] 248 - ?.records || 0 249 : null 250 ); 251 }, [links]); ··· 388 389 hasPerformedInitialLayout.current = true; 390 } 391 - 392 // todo idk what to do with this 393 // eslint-disable-next-line react-hooks/set-state-in-effect 394 setLayoutReady(true); ··· 396 }, [parents, layoutReady, showMainPostRoute]); 397 398 399 - const [slingshoturl] = useAtom(slingshotURLAtom) 400 - 401 React.useEffect(() => { 402 if (parentsLoading || !showMainPostRoute) { 403 setLayoutReady(false); ··· 412 const directparent = mainPost?.value.reply?.parent.uri; 413 414 React.useEffect(() => { 415 if (!mainPost?.value?.reply?.parent?.uri) { 416 setParents([]); 417 return; ··· 420 let ignore = false; 421 const fetchParents = async () => { 422 setParentsLoading(true); 423 - const parentChain: ({uri: string;cid: string;value: any;} | undefined)[] = []; 424 let currentParentUri = mainPost?.value.reply?.parent.uri; 425 const MAX_PARENTS = 25; 426 let safetyCounter = 0; 427 428 while (currentParentUri && safetyCounter < MAX_PARENTS) { 429 try { 430 - const parentPost = await queryClient.fetchQuery( 431 - constructPostQuery(currentParentUri, slingshoturl) 432 - ); 433 if (!parentPost) break; 434 parentChain.push(parentPost); 435 currentParentUri = parentPost.value?.reply?.parent?.uri; 436 } catch (error) { 437 console.error("Failed to fetch a parent post:", error); 438 // its okay to always add one invalid parent then stop 439 - if (currentParentUri){ 440 parentChain.push({ 441 uri: currentParentUri, 442 cid: "sorry", ··· 458 return () => { 459 ignore = true; 460 }; 461 - }, [mainPost, queryClient, slingshoturl]); 462 463 if ((!did || !rkey) && showMainPostRoute) return <div>Invalid post URI</div>; 464 if (isIdentityLoading && showMainPostRoute) return <div>Resolving handle...</div>; ··· 545 /> 546 ); 547 })} 548 - {hasNextPage && ( 549 <button 550 onClick={() => fetchNextPage()} 551 disabled={isFetchingNextPage} ··· 560 </> 561 ); 562 }
··· 1 import { AtUri } from "@atproto/api"; 2 + import { QueryClient, useInfiniteQuery, useQueryClient } from "@tanstack/react-query"; 3 import { createFileRoute, Outlet, useMatchRoute } from "@tanstack/react-router"; 4 + import { useAtom, useAtomValue } from "jotai"; 5 + import { loadable } from "jotai/utils"; 6 import React, { useLayoutEffect } from "react"; 7 8 import { Header } from "~/components/Header"; 9 import { UniversalPostRendererATURILoader } from "~/components/UniversalPostRenderer"; 10 + import { appviewUrlAtom, constellationURLAtom, enableAppViewAtom, slingshotURLAtom } from "~/utils/atoms"; 11 //import { usePersistentStore } from '~/providers/PersistentStoreProvider'; 12 import { 13 constructPostQuery, 14 + constructSingularAVPostQuery, 15 type linksAllResponse, 16 type linksRecordsResponse, 17 useQueryConstellation, 18 + useQueryFastAVIdentity, 19 + //useQueryIdentity, 20 useQueryPost, 21 yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks, 22 } from "~/utils/useQuery"; ··· 192 // }; 193 // }, [atUri]); 194 195 + const [slingshoturl] = useAtom(slingshotURLAtom); 196 + 197 const { 198 data: identity, 199 isLoading: isIdentityLoading, 200 error: identityError, 201 + } = useQueryFastAVIdentity(showMainPostRoute ? did : undefined, slingshoturl, queryClient, true); 202 203 const resolvedDid = did.startsWith("did:") ? did : identity?.did; 204 ··· 212 213 const { data: mainPost } = useQueryPost(showMainPostRoute ? atUri : undefined); 214 215 + console.log("atUri", atUri) 216 + 217 const opdid = React.useMemo( 218 () => 219 atUri ··· 223 ); 224 225 // @ts-expect-error i hate overloads 226 + const { data: links } = useQueryConstellation(atUri && showMainPostRoute ? { 227 method: "/links/all", 228 target: atUri, 229 } : { 230 method: "undefined", 231 target: "" 232 + }) as { data: linksAllResponse | undefined }; 233 234 //const [likes, setLikes] = React.useState<number | null>(null); 235 //const [reposts, setReposts] = React.useState<number | null>(null); ··· 250 setReplyCount( 251 links 252 ? links?.links?.["app.bsky.feed.post"]?.[".reply.parent.uri"] 253 + ?.records || 0 254 : null 255 ); 256 }, [links]); ··· 393 394 hasPerformedInitialLayout.current = true; 395 } 396 + 397 // todo idk what to do with this 398 // eslint-disable-next-line react-hooks/set-state-in-effect 399 setLayoutReady(true); ··· 401 }, [parents, layoutReady, showMainPostRoute]); 402 403 404 + const [isAppviewEnabled] = useAtom(enableAppViewAtom); 405 + const loadablePrefs = useAtomValue(loadable(enableAppViewAtom)) 406 + React.useEffect(() => { 407 + console.log("why is this fucked isAppviewEnabled?:", isAppviewEnabled); 408 + },[isAppviewEnabled]) 409 + const [appviewUrl] = useAtom(appviewUrlAtom); 410 + //const [slingshoturl] = useAtom(slingshotURLAtom) 411 + 412 React.useEffect(() => { 413 if (parentsLoading || !showMainPostRoute) { 414 setLayoutReady(false); ··· 423 const directparent = mainPost?.value.reply?.parent.uri; 424 425 React.useEffect(() => { 426 + console.log("parent fetching useeffect called!") 427 + // if (loadablePrefs.state !== "hasData") { 428 + // setParentsLoading(true); 429 + // return; 430 + // } 431 if (!mainPost?.value?.reply?.parent?.uri) { 432 setParents([]); 433 return; ··· 436 let ignore = false; 437 const fetchParents = async () => { 438 setParentsLoading(true); 439 + const parentChain: ({ uri: string; cid: string; value: any; } | undefined)[] = []; 440 let currentParentUri = mainPost?.value.reply?.parent.uri; 441 const MAX_PARENTS = 25; 442 let safetyCounter = 0; 443 444 while (currentParentUri && safetyCounter < MAX_PARENTS) { 445 try { 446 + const parentPost = await getProfilePostTryFail({isAppviewEnabled, appviewUrl, queryClient, currentParentUri, slingshoturl}) 447 if (!parentPost) break; 448 parentChain.push(parentPost); 449 currentParentUri = parentPost.value?.reply?.parent?.uri; 450 } catch (error) { 451 console.error("Failed to fetch a parent post:", error); 452 // its okay to always add one invalid parent then stop 453 + if (currentParentUri) { 454 parentChain.push({ 455 uri: currentParentUri, 456 cid: "sorry", ··· 472 return () => { 473 ignore = true; 474 }; 475 + }, [appviewUrl, isAppviewEnabled, loadablePrefs.state, mainPost, queryClient, slingshoturl]); 476 477 if ((!did || !rkey) && showMainPostRoute) return <div>Invalid post URI</div>; 478 if (isIdentityLoading && showMainPostRoute) return <div>Resolving handle...</div>; ··· 559 /> 560 ); 561 })} 562 + {hasNextPage && ( 563 <button 564 onClick={() => fetchNextPage()} 565 disabled={isFetchingNextPage} ··· 574 </> 575 ); 576 } 577 + 578 + 579 + async function getProfilePostTryFail({ 580 + isAppviewEnabled, 581 + appviewUrl, 582 + queryClient, 583 + currentParentUri, 584 + slingshoturl, 585 + }: { 586 + isAppviewEnabled?: boolean, 587 + appviewUrl?: string, 588 + queryClient: QueryClient, 589 + currentParentUri: string, 590 + slingshoturl: string, 591 + }): Promise<{ 592 + uri: string, 593 + cid: string, 594 + value: any 595 + } | undefined> { 596 + try { 597 + if (isAppviewEnabled && appviewUrl) { 598 + console.log("why is this called? isAppviewEnabled:",isAppviewEnabled," appviewUrl:",appviewUrl) 599 + const result = await queryClient.fetchQuery( 600 + constructSingularAVPostQuery({ aturi: currentParentUri, avurl: appviewUrl, instantBypass: true }) 601 + ) 602 + if (result?.uri && result?.cid && result?.record) { 603 + return { 604 + uri: result?.uri, 605 + cid: result?.cid, 606 + value: result?.record as any 607 + } 608 + } else { 609 + throw "whatever"; 610 + } 611 + } else { 612 + throw "sure"; 613 + } 614 + } catch { 615 + console.log("whatever why is this called? isAppviewEnabled:",isAppviewEnabled," appviewUrl:",appviewUrl) 616 + return await queryClient.fetchQuery( 617 + constructPostQuery(currentParentUri, slingshoturl)) 618 + } 619 + }
+1 -1
src/routes/settings.tsx
··· 205 <SwitchSetting 206 atom={enableAppViewAtom} 207 title={"AppView-First"} 208 - description={"Prioritize using an AppView to hydrate posts & profiles before using microcosm"} 209 //init={false} 210 /> 211 <div className={`${isAppViewEnabled ? "" : "opacity-50 pointer-events-none"}`}>
··· 205 <SwitchSetting 206 atom={enableAppViewAtom} 207 title={"AppView-First"} 208 + description={"Prioritize using an AppView to fetch posts before using microcosm"} 209 //init={false} 210 /> 211 <div className={`${isAppViewEnabled ? "" : "opacity-50 pointer-events-none"}`}>
+3 -1
src/utils/followState.ts
··· 134 user: string; 135 collection: string; 136 path: string; 137 }): { 138 uris: string[], 139 isLoading: boolean; ··· 152 customkey: params.collection.includes("reddwarf.poll.vote") 153 ? "constellation-polls" 154 : undefined, 155 } 156 - : { method: "undefined", target: "whatever" }, 157 // overloading sucks so much 158 ) as UseQueryResult<linksRecordsResponse | undefined, Error>; 159 if (!params || !params.user) return {
··· 134 user: string; 135 collection: string; 136 path: string; 137 + enabled?: boolean; 138 }): { 139 uris: string[], 140 isLoading: boolean; ··· 153 customkey: params.collection.includes("reddwarf.poll.vote") 154 ? "constellation-polls" 155 : undefined, 156 + enabled: params.enabled || false, 157 } 158 + : { method: "undefined", target: "whatever", enabled: false }, 159 // overloading sucks so much 160 ) as UseQueryResult<linksRecordsResponse | undefined, Error>; 161 if (!params || !params.user) return {
+74 -2
src/utils/useQuery.ts
··· 7 useInfiniteQuery, 8 useQueries, 9 useQuery, 10 type UseQueryResult, 11 } from "@tanstack/react-query"; 12 import { create, windowScheduler } from "@yornaath/batshit"; ··· 73 export function useQueryIdentity(didorhandle?: string) { 74 const [slingshoturl] = useAtom(slingshotURLAtom); 75 return useQuery(constructIdentityQuery(didorhandle, slingshoturl)); 76 } 77 78 export function constructPostQuery(uri?: string, slingshoturl?: string) { ··· 1398 type SingularAVPostQuery = { 1399 aturi: string, 1400 avurl: string, 1401 } 1402 type SingularAVPostResult = ATPAPI.AppBskyFeedDefs.PostView 1403 ··· 1533 ); 1534 1535 export function constructSingularAVPostQuery(options: SingularAVPostQuery) { 1536 - const { aturi, avurl } = options; 1537 1538 return queryOptions({ 1539 - queryKey: ["__volatile","savpq", aturi], 1540 1541 enabled: !!aturi && !!avurl, 1542 ··· 1546 // throw result.error 1547 // } 1548 // return result; 1549 const result = (await postquerymerge 1550 .fetch(options))as SingularAVPostResult; 1551 // .catch(
··· 7 useInfiniteQuery, 8 useQueries, 9 useQuery, 10 + //useQueryClient, 11 type UseQueryResult, 12 } from "@tanstack/react-query"; 13 import { create, windowScheduler } from "@yornaath/batshit"; ··· 74 export function useQueryIdentity(didorhandle?: string) { 75 const [slingshoturl] = useAtom(slingshotURLAtom); 76 return useQuery(constructIdentityQuery(didorhandle, slingshoturl)); 77 + } 78 + 79 + export function constructFastAVIdentityQuery( 80 + didorhandle?: string, 81 + slingshoturl?: string, 82 + queryClient?: QueryClient, 83 + enabled?: boolean 84 + ) { 85 + return queryOptions({ 86 + queryKey: ["identity", didorhandle], 87 + queryFn: async () => { 88 + try { 89 + console.log("whathuh trying", ["savpq", didorhandle]) 90 + if (!queryClient) throw "whatever" 91 + const datas = queryClient.getQueriesData<SingularAVPostResult | undefined>({ 92 + queryKey: ["savpq", didorhandle], 93 + }) 94 + console.log("whathuh checking", datas) 95 + const data = datas[0][1]; 96 + if (!data) { 97 + throw "whatever" 98 + } 99 + //const parsedaturi = new ATPAPI.AtUri(data.uri) 100 + console.log("whathuh success") 101 + return { 102 + did: data.author.did, 103 + handle: data.author.handle 104 + } 105 + } catch { 106 + console.log("whathuh failure") 107 + if (!didorhandle) return undefined as undefined; 108 + const res = await fetch( 109 + `https://${slingshoturl}/xrpc/com.bad-example.identity.resolveMiniDoc?identifier=${encodeURIComponent(didorhandle)}`, 110 + ); 111 + if (!res.ok) throw new Error("Failed to fetch post"); 112 + try { 113 + return (await res.json()) as { 114 + did: string; 115 + handle: string; 116 + pds: string; 117 + signing_key: string; 118 + }; 119 + } catch (_e) { 120 + return undefined; 121 + } 122 + } 123 + }, 124 + enabled, 125 + staleTime: /*0,//*/ 5 * 60 * 1000, // 5 minutes 126 + gcTime: /*0//*/ 5 * 60 * 1000, 127 + }); 128 + } 129 + 130 + 131 + export function useQueryFastAVIdentity(didorhandle?: string, slingshoturl?: string, queryClient?: QueryClient, enabled: boolean = true) { 132 + return useQuery(constructFastAVIdentityQuery(didorhandle, slingshoturl, queryClient, enabled)); 133 } 134 135 export function constructPostQuery(uri?: string, slingshoturl?: string) { ··· 1455 type SingularAVPostQuery = { 1456 aturi: string, 1457 avurl: string, 1458 + instantBypass?: boolean, 1459 } 1460 type SingularAVPostResult = ATPAPI.AppBskyFeedDefs.PostView 1461 ··· 1591 ); 1592 1593 export function constructSingularAVPostQuery(options: SingularAVPostQuery) { 1594 + const { aturi, avurl, instantBypass } = options; 1595 + const parsedaturi = new ATPAPI.AtUri(aturi) 1596 1597 return queryOptions({ 1598 + queryKey: ["savpq", parsedaturi.host, /*"__volatile", */aturi], 1599 1600 enabled: !!aturi && !!avurl, 1601 ··· 1605 // throw result.error 1606 // } 1607 // return result; 1608 + if (instantBypass) { 1609 + const params = new URLSearchParams(); 1610 + params.append("uris", aturi) 1611 + const url = `${avurl}/xrpc/app.bsky.feed.getPosts?${params.toString()}`; 1612 + 1613 + const res = await fetch(url); 1614 + if (!res.ok) { 1615 + throw new Error(`Labelmerge fetch failed: ${res.status} ${res.statusText}`); 1616 + } 1617 + 1618 + const result = (await res.json()) as ATPAPI.AppBskyFeedGetPosts.OutputSchema; 1619 + return result.posts[0] 1620 + } 1621 const result = (await postquerymerge 1622 .fetch(options))as SingularAVPostResult; 1623 // .catch(