import { Conversation } from "@/components/conversation"; import { Quote } from "@/components/quote"; import { RepostNote } from "@/components/repost"; import { TextNote } from "@/components/text"; import { ArrowRightCircleIcon } from "@lume/icons"; import { type LumeEvent, NostrQuery } from "@lume/system"; import { type ColumnRouteSearch, Kind } from "@lume/types"; import { Spinner } from "@lume/ui"; import * as ScrollArea from "@radix-ui/react-scroll-area"; import { useInfiniteQuery } from "@tanstack/react-query"; import { createFileRoute, redirect } from "@tanstack/react-router"; import { useCallback, useRef } from "react"; import { Virtualizer } from "virtua"; type Topic = { content: string[]; }; export const Route = createFileRoute("/topic")({ validateSearch: (search: Record): ColumnRouteSearch => { return { account: search.account, label: search.label, name: search.name, }; }, beforeLoad: async ({ search }) => { const key = `lume:topic:${search.label}`; const topics: Topic[] = await NostrQuery.getNstore(key); const settings = await NostrQuery.getUserSettings(); if (!topics?.length) { throw redirect({ to: "/create-topic", search: { ...search, redirect: "/topic", }, }); } const hashtags: string[] = []; for (const topic of topics) { hashtags.push(...topic.content); } return { settings, hashtags }; }, component: Screen, }); export function Screen() { const { label, account } = Route.useSearch(); const { hashtags } = Route.useRouteContext(); const { data, isLoading, isFetching, isFetchingNextPage, hasNextPage, fetchNextPage, } = useInfiniteQuery({ queryKey: [label, account], initialPageParam: 0, queryFn: async ({ pageParam }: { pageParam: number }) => { const events = NostrQuery.getHashtagEvents(hashtags, pageParam); return events; }, getNextPageParam: (lastPage) => lastPage?.at(-1)?.created_at - 1, select: (data) => data?.pages.flat(), refetchOnWindowFocus: false, }); const ref = useRef(null); const renderItem = useCallback( (event: LumeEvent) => { if (!event) return; switch (event.kind) { case Kind.Repost: return ; default: { if (event.isConversation) { return ( ); } if (event.isQuote) { return ; } return ; } } }, [data], ); return ( {isFetching && !isLoading && !isFetchingNextPage ? (
Fetching new notes...
) : null} {isLoading ? (
Loading...
) : !data.length ? (
Yo. You're catching up on all the things happening around you.
) : ( data.map((item) => renderItem(item)) )} {data?.length && hasNextPage ? (
) : null}
); }