import { events, commands } from "@/commands.gen"; import { toLumeEvents } from "@/commons"; import { Quote, RepostNote, Spinner, TextNote } from "@/components"; import { LumeEvent } from "@/system"; import { Kind, type Meta } from "@/types"; import { ArrowCircleRight, ArrowUp } from "@phosphor-icons/react"; import * as ScrollArea from "@radix-ui/react-scroll-area"; import { type InfiniteData, useInfiniteQuery } from "@tanstack/react-query"; import { Navigate, createLazyFileRoute, useLocation, } from "@tanstack/react-router"; import { listen } from "@tauri-apps/api/event"; import { getCurrentWindow } from "@tauri-apps/api/window"; import { memo, useCallback, useEffect, useRef, useState, useTransition, } from "react"; import { Virtualizer } from "virtua"; type Payload = { raw: string; parsed: Meta; }; export const Route = createLazyFileRoute("/columns/_layout/newsfeed")({ component: Screen, }); export function Screen() { const { queryClient } = Route.useRouteContext(); const { label, account } = Route.useSearch(); const { data, isLoading, isError, isFetching, isFetchingNextPage, hasNextPage, fetchNextPage, } = useInfiniteQuery({ queryKey: [label, account], initialPageParam: 0, queryFn: async ({ pageParam }: { pageParam: number }) => { const until = pageParam > 0 ? pageParam.toString() : undefined; const res = await commands.getEventsFromContacts(until); if (res.status === "error") { throw new Error(res.error); } return toLumeEvents(res.data); }, getNextPageParam: (lastPage) => lastPage?.at?.(-1)?.created_at - 1, select: (data) => data?.pages.flat(), }); const location = useLocation(); const ref = useRef(null); const renderItem = useCallback( (event: LumeEvent) => { if (!event) return; switch (event.kind) { case Kind.Repost: return ; default: { if (event.isQuote) { return ; } else { return ; } } } }, [data], ); useEffect(() => { const unlisten = listen("synchronized", async () => { await queryClient.invalidateQueries({ queryKey: [label, account] }); }); return () => { unlisten.then((f) => f()); }; }, []); if (isError) { return ( ); } return ( {isFetching && !isLoading && !isFetchingNextPage ? (
Getting new notes...
) : null} {isLoading ? (
Loading...
) : !data.length ? (
🎉 Yo. You're catching up on all latest notes.
) : ( data.map((item) => renderItem(item)) )} {hasNextPage ? (
) : null}
); } const Listerner = memo(function Listerner() { const { queryClient } = Route.useRouteContext(); const { label, account } = Route.useSearch(); const [lumeEvents, setLumeEvents] = useState([]); const [isPending, startTransition] = useTransition(); const queryStatus = queryClient.getQueryState([label, account]); const pushNewEvents = () => { startTransition(() => { queryClient.setQueryData( [label, account], (oldData: InfiniteData | undefined) => { if (oldData) { const firstPage = oldData.pages[0]; const newPage = [...lumeEvents, ...firstPage]; return { ...oldData, pages: [newPage, ...oldData.pages.slice(1)], }; } }, ); // Reset array setLumeEvents([]); return; }); }; useEffect(() => { events.subscription .emit({ label, kind: "Subscribe", event_id: undefined }) .then(() => console.log("Subscribe: ", label)); return () => { events.subscription .emit({ label, kind: "Unsubscribe", event_id: undefined, }) .then(() => console.log("Unsubscribe: ", label)); }; }, []); useEffect(() => { const unlisten = getCurrentWindow().listen("event", (data) => { const event = LumeEvent.from(data.payload.raw, data.payload.parsed); setLumeEvents((prev) => [event, ...prev]); }); return () => { unlisten.then((f) => f()); }; }, []); if (lumeEvents.length && queryStatus.fetchStatus !== "fetching") { return (
); } return null; });