import type { Agent } from "@atproto/api"; import { useQueryClient } from "@tanstack/react-query"; import { createFileRoute, useSearch } from "@tanstack/react-router"; import { useAtom } from "jotai"; import { useEffect,useMemo } from "react"; import { Header } from "~/components/Header"; import { Import } from "~/components/Import"; import { ReusableTabRoute, useReusableTabScrollRestore, } from "~/components/ReusableTabRoute"; import { UniversalPostRendererATURILoader } from "~/components/UniversalPostRenderer"; import { useAuth } from "~/providers/UnifiedAuthProvider"; import { lycanURLAtom } from "~/utils/atoms"; import { constructLycanRequestIndexQuery, useInfiniteQueryLycanSearch, useQueryIdentity, useQueryLycanStatus, } from "~/utils/useQuery"; import { renderSnack } from "./__root"; import { SliderPrimitive } from "./settings"; export const Route = createFileRoute("/search")({ component: Search, }); export function Search() { const queryClient = useQueryClient(); const { agent, status } = useAuth(); const { data: identity } = useQueryIdentity(agent?.did); const [lycandomain] = useAtom(lycanURLAtom); const lycanExists = lycandomain !== ""; const { data: lycanstatusdata, refetch } = useQueryLycanStatus(); const lycanIndexed = lycanstatusdata?.status === "finished" || false; const lycanIndexing = lycanstatusdata?.status === "in_progress" || false; const lycanIndexingProgress = lycanIndexing ? lycanstatusdata?.progress : undefined; const authed = status === "signedIn"; const lycanReady = lycanExists && lycanIndexed && authed; const { q }: { q: string } = useSearch({ from: "/search" }); // auto-refetch Lycan status until ready useEffect(() => { if (!lycanExists || !authed) return; if (lycanReady) return; const interval = setInterval(() => { refetch(); }, 3000); return () => clearInterval(interval); }, [lycanExists, authed, lycanReady, refetch]); const maintext = !lycanExists ? "Sorry we dont have search. But instead, you can load some of these types of content into Red Dwarf:" : authed ? lycanReady ? "Lycan Search is enabled and ready! Type to search posts you've interacted with in the past. You can also load some of these types of content into Red Dwarf:" : "Sorry, while Lycan Search is enabled, you are not indexed. Index below please. You can load some of these types of content into Red Dwarf:" : "Sorry, while Lycan Search is enabled, you are unauthed. Please log in to use Lycan. You can load some of these types of content into Red Dwarf:"; async function index(opts: { agent?: Agent; isAuthed: boolean; pdsUrl?: string; feedServiceDid?: string; }) { renderSnack({ title: "Registering account...", }); try { const response = await queryClient.fetchQuery( constructLycanRequestIndexQuery(opts) ); if ( response?.message !== "Import has already started" && response?.message !== "Import has been scheduled" ) { renderSnack({ title: "Registration failed!", description: "Unknown server error (2)", }); } else { renderSnack({ title: "Succesfully sent registration request!", description: "Please wait for the server to index your account", }); refetch(); } } catch { renderSnack({ title: "Registration failed!", description: "Unknown server error (1)", }); } } return ( <>
{ if (window.history.length > 1) { window.history.back(); } else { window.location.assign("/"); } }} />

{maintext}

  • Bluesky URLs (from supported clients) (like{" "} bsky.app or{" "} deer.social).
  • AT-URIs (e.g.,{" "} at://did:example/collection/item ).
  • User Handles (like{" "} @username.bsky.social).
  • DIDs (Decentralized Identifiers, starting with{" "} did:).

Simply paste one of these into the import field above and press Enter to load the content.

{lycanExists && authed && !lycanReady ? ( !lycanIndexing ? (
) : (
indexing...
) ) : ( <> )}
{q ? : <>} ); } function SearchTabs({ query }: { query: string }) { return (
, Reposts: , Quotes: , Pins: , }} />
); } function LycanTab({ query, type, }: { query: string; type: "likes" | "pins" | "reposts" | "quotes"; }) { useReusableTabScrollRestore("search" + query); const { data: postsData, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading: arePostsLoading, } = useInfiniteQueryLycanSearch({ query: query, type: type }); const posts = useMemo( () => postsData?.pages.flatMap((page) => { if (page) { return page.posts; } else { return []; } }) ?? [], [postsData] ); return ( <> {/*
Posts
*/}
{posts.map((post) => ( ))}
{/* Loading and "Load More" states */} {arePostsLoading && posts.length === 0 && (
Loading posts...
)} {isFetchingNextPage && (
Loading more...
)} {hasNextPage && !isFetchingNextPage && ( )} {posts.length === 0 && !arePostsLoading && (
No posts found.
)} ); return <>; }