From 61ce2144ca06f616e190e96e3c8484b3ae2109c2 Mon Sep 17 00:00:00 2001 From: rimar1337 <132627503+rimar1337@users.noreply.github.com> Date: Thu, 30 Oct 2025 02:15:49 +0000 Subject: [PATCH] post interactions --- src/auto-imports.d.ts | 1 + src/routeTree.gen.ts | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/components/UniversalPostRenderer.tsx | 22 ++++++++++++++++++---- src/routes/notifications.tsx | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------------- src/utils/useQuery.ts | 10 +++++----- src/routes/profile.$did/post.$rkey.liked-by.tsx | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/routes/profile.$did/post.$rkey.quotes.tsx | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/routes/profile.$did/post.$rkey.reposted-by.tsx | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/routes/profile.$did/post.$rkey.tsx | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------ 9 file(s) changed, 629 insertion(s)(+), 161 deletion(s)(-) diff --git a/src/auto-imports.d.ts b/src/auto-imports.d.ts --- a/src/auto-imports.d.ts +++ b/src/auto-imports.d.ts @@ -19,5 +19,6 @@ const IconMaterialSymbolsTag: typeof import('~icons/material-symbols/tag.jsx').default const IconMdiAccountCircle: typeof import('~icons/mdi/account-circle.jsx').default const IconMdiAccountPlus: typeof import('~icons/mdi/account-plus.jsx').default + const IconMdiMessageReplyTextOutline: typeof import('~icons/mdi/message-reply-text-outline.jsx').default const IconMdiPencilOutline: typeof import('~icons/mdi/pencil-outline.jsx').default } diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -21,6 +21,9 @@ import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b' import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a' import { Route as ProfileDidPostRkeyRouteImport } from './routes/profile.$did/post.$rkey' +import { Route as ProfileDidPostRkeyRepostedByRouteImport } from './routes/profile.$did/post.$rkey.reposted-by' +import { Route as ProfileDidPostRkeyQuotesRouteImport } from './routes/profile.$did/post.$rkey.quotes' +import { Route as ProfileDidPostRkeyLikedByRouteImport } from './routes/profile.$did/post.$rkey.liked-by' import { Route as ProfileDidPostRkeyImageIRouteImport } from './routes/profile.$did/post.$rkey.image.$i' const SettingsRoute = SettingsRouteImport.update({ @@ -84,6 +87,24 @@ path: '/profile/$did/post/$rkey', getParentRoute: () => rootRouteImport, } as any) +const ProfileDidPostRkeyRepostedByRoute = + ProfileDidPostRkeyRepostedByRouteImport.update({ + id: '/reposted-by', + path: '/reposted-by', + getParentRoute: () => ProfileDidPostRkeyRoute, + } as any) +const ProfileDidPostRkeyQuotesRoute = + ProfileDidPostRkeyQuotesRouteImport.update({ + id: '/quotes', + path: '/quotes', + getParentRoute: () => ProfileDidPostRkeyRoute, + } as any) +const ProfileDidPostRkeyLikedByRoute = + ProfileDidPostRkeyLikedByRouteImport.update({ + id: '/liked-by', + path: '/liked-by', + getParentRoute: () => ProfileDidPostRkeyRoute, + } as any) const ProfileDidPostRkeyImageIRoute = ProfileDidPostRkeyImageIRouteImport.update({ id: '/image/$i', @@ -102,6 +123,9 @@ '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute '/profile/$did': typeof ProfileDidIndexRoute '/profile/$did/post/$rkey': typeof ProfileDidPostRkeyRouteWithChildren + '/profile/$did/post/$rkey/liked-by': typeof ProfileDidPostRkeyLikedByRoute + '/profile/$did/post/$rkey/quotes': typeof ProfileDidPostRkeyQuotesRoute + '/profile/$did/post/$rkey/reposted-by': typeof ProfileDidPostRkeyRepostedByRoute '/profile/$did/post/$rkey/image/$i': typeof ProfileDidPostRkeyImageIRoute } export interface FileRoutesByTo { @@ -115,6 +139,9 @@ '/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute '/profile/$did': typeof ProfileDidIndexRoute '/profile/$did/post/$rkey': typeof ProfileDidPostRkeyRouteWithChildren + '/profile/$did/post/$rkey/liked-by': typeof ProfileDidPostRkeyLikedByRoute + '/profile/$did/post/$rkey/quotes': typeof ProfileDidPostRkeyQuotesRoute + '/profile/$did/post/$rkey/reposted-by': typeof ProfileDidPostRkeyRepostedByRoute '/profile/$did/post/$rkey/image/$i': typeof ProfileDidPostRkeyImageIRoute } export interface FileRoutesById { @@ -131,6 +158,9 @@ '/_pathlessLayout/_nested-layout/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute '/profile/$did/': typeof ProfileDidIndexRoute '/profile/$did/post/$rkey': typeof ProfileDidPostRkeyRouteWithChildren + '/profile/$did/post/$rkey/liked-by': typeof ProfileDidPostRkeyLikedByRoute + '/profile/$did/post/$rkey/quotes': typeof ProfileDidPostRkeyQuotesRoute + '/profile/$did/post/$rkey/reposted-by': typeof ProfileDidPostRkeyRepostedByRoute '/profile/$did/post/$rkey/image/$i': typeof ProfileDidPostRkeyImageIRoute } export interface FileRouteTypes { @@ -146,6 +176,9 @@ | '/route-b' | '/profile/$did' | '/profile/$did/post/$rkey' + | '/profile/$did/post/$rkey/liked-by' + | '/profile/$did/post/$rkey/quotes' + | '/profile/$did/post/$rkey/reposted-by' | '/profile/$did/post/$rkey/image/$i' fileRoutesByTo: FileRoutesByTo to: @@ -159,6 +192,9 @@ | '/route-b' | '/profile/$did' | '/profile/$did/post/$rkey' + | '/profile/$did/post/$rkey/liked-by' + | '/profile/$did/post/$rkey/quotes' + | '/profile/$did/post/$rkey/reposted-by' | '/profile/$did/post/$rkey/image/$i' id: | '__root__' @@ -174,6 +210,9 @@ | '/_pathlessLayout/_nested-layout/route-b' | '/profile/$did/' | '/profile/$did/post/$rkey' + | '/profile/$did/post/$rkey/liked-by' + | '/profile/$did/post/$rkey/quotes' + | '/profile/$did/post/$rkey/reposted-by' | '/profile/$did/post/$rkey/image/$i' fileRoutesById: FileRoutesById } @@ -275,6 +314,27 @@ preLoaderRoute: typeof ProfileDidPostRkeyRouteImport parentRoute: typeof rootRouteImport } + '/profile/$did/post/$rkey/reposted-by': { + id: '/profile/$did/post/$rkey/reposted-by' + path: '/reposted-by' + fullPath: '/profile/$did/post/$rkey/reposted-by' + preLoaderRoute: typeof ProfileDidPostRkeyRepostedByRouteImport + parentRoute: typeof ProfileDidPostRkeyRoute + } + '/profile/$did/post/$rkey/quotes': { + id: '/profile/$did/post/$rkey/quotes' + path: '/quotes' + fullPath: '/profile/$did/post/$rkey/quotes' + preLoaderRoute: typeof ProfileDidPostRkeyQuotesRouteImport + parentRoute: typeof ProfileDidPostRkeyRoute + } + '/profile/$did/post/$rkey/liked-by': { + id: '/profile/$did/post/$rkey/liked-by' + path: '/liked-by' + fullPath: '/profile/$did/post/$rkey/liked-by' + preLoaderRoute: typeof ProfileDidPostRkeyLikedByRouteImport + parentRoute: typeof ProfileDidPostRkeyRoute + } '/profile/$did/post/$rkey/image/$i': { id: '/profile/$did/post/$rkey/image/$i' path: '/image/$i' @@ -316,10 +376,16 @@ ) interface ProfileDidPostRkeyRouteChildren { + ProfileDidPostRkeyLikedByRoute: typeof ProfileDidPostRkeyLikedByRoute + ProfileDidPostRkeyQuotesRoute: typeof ProfileDidPostRkeyQuotesRoute + ProfileDidPostRkeyRepostedByRoute: typeof ProfileDidPostRkeyRepostedByRoute ProfileDidPostRkeyImageIRoute: typeof ProfileDidPostRkeyImageIRoute } const ProfileDidPostRkeyRouteChildren: ProfileDidPostRkeyRouteChildren = { + ProfileDidPostRkeyLikedByRoute: ProfileDidPostRkeyLikedByRoute, + ProfileDidPostRkeyQuotesRoute: ProfileDidPostRkeyQuotesRoute, + ProfileDidPostRkeyRepostedByRoute: ProfileDidPostRkeyRepostedByRoute, ProfileDidPostRkeyImageIRoute: ProfileDidPostRkeyImageIRoute, } diff --git a/src/components/UniversalPostRenderer.tsx b/src/components/UniversalPostRenderer.tsx --- a/src/components/UniversalPostRenderer.tsx +++ b/src/components/UniversalPostRenderer.tsx @@ -41,6 +41,7 @@ ref?: React.Ref; dataIndexPropPass?: number; nopics?: boolean; + concise?: boolean; lightboxCallback?: (d: LightboxProps) => void; maxReplies?: number; isQuote?: boolean; @@ -152,6 +153,7 @@ ref, dataIndexPropPass, nopics, + concise, lightboxCallback, maxReplies, isQuote, @@ -536,6 +538,7 @@ ref={ref} dataIndexPropPass={dataIndexPropPass} nopics={nopics} + concise={concise} lightboxCallback={lightboxCallback} maxReplies={maxReplies} isQuote={isQuote} @@ -567,6 +570,7 @@ ref={ref} dataIndexPropPass={dataIndexPropPass} nopics={nopics} + concise={concise} lightboxCallback={lightboxCallback} maxReplies={ maxReplies && maxReplies > 0 ? maxReplies - 1 : undefined @@ -636,6 +640,7 @@ ref, dataIndexPropPass, nopics, + concise, lightboxCallback, maxReplies, isQuote, @@ -657,6 +662,7 @@ ref?: React.Ref; dataIndexPropPass?: number; nopics?: boolean; + concise?: boolean; lightboxCallback?: (d: LightboxProps) => void; maxReplies?: number; isQuote?: boolean; @@ -874,6 +880,7 @@ ref={ref} dataIndexPropPass={dataIndexPropPass} nopics={nopics} + concise={concise} lightboxCallback={lightboxCallback} maxReplies={maxReplies} isQuote={isQuote} @@ -1327,6 +1334,7 @@ ref, dataIndexPropPass, nopics, + concise, lightboxCallback, maxReplies, }: { @@ -1353,6 +1361,7 @@ ref?: React.Ref; dataIndexPropPass?: number; nopics?: boolean; + concise?: boolean; lightboxCallback?: (d: LightboxProps) => void; maxReplies?: number; }) { @@ -1759,12 +1768,17 @@
0*/ ? 0 : 8, + marginBottom: !post.embed || concise ? 0 : 8, whiteSpace: "pre-wrap", textAlign: "left", overflowWrap: "anywhere", wordBreak: "break-word", - //color: theme.text, + ...(concise && { + display: "-webkit-box", + WebkitBoxOrient: "vertical", + WebkitLineClamp: 2, + overflow: "hidden", + }), }} className="text-gray-900 dark:text-gray-100" > @@ -1787,7 +1801,7 @@ )}
- {post.embed && depth < 1 ? ( + {post.embed && depth < 1 && !concise ? ( )} -
+
<> {expanded && (
{ - const likes = - links?.links?.["app.bsky.feed.like"]?.[".subject.uri"]?.records || 0; - const replies = - links?.links?.["app.bsky.feed.post"]?.[".reply.parent.uri"]?.records || 0; - const reposts = - links?.links?.["app.bsky.feed.repost"]?.[".subject.uri"]?.records || 0; - const quotes1 = - links?.links?.["app.bsky.feed.post"]?.[".embed.record.uri"]?.records || 0; - const quotes2 = - links?.links?.["app.bsky.feed.post"]?.[".embed.record.record.uri"] - ?.records || 0; + const likes = + links?.links?.["app.bsky.feed.like"]?.[".subject.uri"]?.records || 0; + const replies = + links?.links?.["app.bsky.feed.post"]?.[".reply.parent.uri"]?.records || 0; + const reposts = + links?.links?.["app.bsky.feed.repost"]?.[".subject.uri"]?.records || 0; + const quotes1 = + links?.links?.["app.bsky.feed.post"]?.[".embed.record.uri"]?.records || 0; + const quotes2 = + links?.links?.["app.bsky.feed.post"]?.[".embed.record.record.uri"] + ?.records || 0; + const quotes = quotes1 + quotes2; - const totals = { - likes, - replies, - reposts, - quotes: quotes1 + quotes2, - }; - - const list = ( - [ - ["reply", totals.replies], - ["repost", totals.reposts], - ["like", totals.likes], - ["quote", totals.quotes], - ] as const - ).filter(([, count]) => count > 0); - - return { totals, list }; - }, [links]); + const all = likes + replies + reposts + quotes; return ( -
-
+
+
-
-
- {interactions.list.map(([type, count]) => ( - - ))} +
+ + + + + {!all && ( +
+ No interactions yet. +
+ )} +
); @@ -340,25 +355,49 @@ uri: string; count: number; }) { + if (!count) return <>; + const aturi = new AtUri(uri); return ( -
+ {type === "like" ? ( ) : type === "repost" ? ( ) : type === "reply" ? ( + ) : type === "quote" ? ( + ) : ( <> )} {type} {/* bad grammar replys */} {count > 1 ? "s" : ""}
{count} -
+ ); } -function NotificationItem({ notification }: { notification: string }) { +export function NotificationItem({ notification }: { notification: string }) { const aturi = new AtUri(notification); const navigate = useNavigate(); const { data: identity } = useQueryIdentity(aturi.host); @@ -381,7 +420,7 @@ return (
aturi && navigate({ @@ -390,13 +429,15 @@ }) } > -
+ {/*
{aturi.collection === "app.bsky.graph.follow" ? ( + ) : aturi.collection === "app.bsky.feed.like" ? ( + ) : ( <> )} -
+
*/} {profile ? ( )} -
-
- +
+
+ {profile?.displayName || identity?.handle || "Someone"} - + @{identity?.handle}
@@ -428,21 +469,20 @@ ); } - -const EmptyState = ({ text }: { text: string }) => ( +export const EmptyState = ({ text }: { text: string }) => (
{text}
); -const LoadingState = ({ text }: { text: string }) => ( +export const LoadingState = ({ text }: { text: string }) => (
{text}
); -const ErrorState = ({ error }: { error: unknown }) => ( +export const ErrorState = ({ error }: { error: unknown }) => (
Error: {(error as Error)?.message || "Something went wrong."}
-); \ No newline at end of file +); diff --git a/src/utils/useQuery.ts b/src/utils/useQuery.ts --- a/src/utils/useQuery.ts +++ b/src/utils/useQuery.ts @@ -352,7 +352,7 @@ ); } -type linksRecord = { +export type linksRecord = { did: string; collection: string; rkey: string; @@ -634,10 +634,10 @@ collection: string path: string }) { - console.log( - 'yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks', - query, - ) + // console.log( + // 'yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks', + // query, + // ) return infiniteQueryOptions({ enabled: !!query?.target, diff --git a/src/routes/profile.$did/post.$rkey.liked-by.tsx b/src/routes/profile.$did/post.$rkey.liked-by.tsx new file mode 100644 --- /dev/null +++ b/src/routes/profile.$did/post.$rkey.liked-by.tsx @@ -0,0 +1,100 @@ +import { useInfiniteQuery } from "@tanstack/react-query"; +import { createFileRoute } from "@tanstack/react-router"; +import { useAtom } from "jotai"; +import React from "react"; + +import { Header } from "~/components/Header"; +import { constellationURLAtom } from "~/utils/atoms"; +import { useQueryIdentity, yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks } from "~/utils/useQuery"; + +import { + EmptyState, + ErrorState, + LoadingState, + NotificationItem, +} from "../notifications"; + +export const Route = createFileRoute("/profile/$did/post/$rkey/liked-by")({ + component: RouteComponent, +}); + +function RouteComponent() { + const { did, rkey } = Route.useParams(); + const { data: identity } = useQueryIdentity(did); + const atUri = identity?.did && rkey ? `at://${decodeURIComponent(identity.did)}/app.bsky.feed.post/${rkey}` : ''; + + const [constellationurl] = useAtom(constellationURLAtom); + const infinitequeryresults = useInfiniteQuery({ + ...yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks( + { + constellation: constellationurl, + method: "/links", + target: atUri, + collection: "app.bsky.feed.like", + path: ".subject.uri", + } + ), + enabled: !!atUri, + }); + + const { + data: infiniteLikesData, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + isLoading, + isError, + error, + } = infinitequeryresults; + + const likesAturis = React.useMemo(() => { + // Get all replies from the standard infinite query + return ( + infiniteLikesData?.pages.flatMap( + (page) => + page?.linking_records.map( + (r) => `at://${r.did}/${r.collection}/${r.rkey}` + ) ?? [] + ) ?? [] + ); + }, [infiniteLikesData]); + + return ( + <> +
{ + if (window.history.length > 1) { + window.history.back(); + } else { + window.location.assign("/"); + } + }} + /> + + <> + {(() => { + if (isLoading) return ; + if (isError) return ; + + if (!likesAturis?.length) + return ; + })()} + + + {likesAturis.map((m) => ( + + ))} + + {hasNextPage && ( + + )} + + ); +} diff --git a/src/routes/profile.$did/post.$rkey.quotes.tsx b/src/routes/profile.$did/post.$rkey.quotes.tsx new file mode 100644 --- /dev/null +++ b/src/routes/profile.$did/post.$rkey.quotes.tsx @@ -0,0 +1,141 @@ +import { useInfiniteQuery } from "@tanstack/react-query"; +import { createFileRoute } from "@tanstack/react-router"; +import { useAtom } from "jotai"; +import React from "react"; + +import { Header } from "~/components/Header"; +import { UniversalPostRendererATURILoader } from "~/components/UniversalPostRenderer"; +import { constellationURLAtom } from "~/utils/atoms"; +import { type linksRecord,useQueryIdentity, yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks } from "~/utils/useQuery"; + +import { + EmptyState, + ErrorState, + LoadingState, +} from "../notifications"; + +export const Route = createFileRoute("/profile/$did/post/$rkey/quotes")({ + component: RouteComponent, +}); + +function RouteComponent() { + const { did, rkey } = Route.useParams(); + const { data: identity } = useQueryIdentity(did); + const atUri = identity?.did && rkey ? `at://${decodeURIComponent(identity.did)}/app.bsky.feed.post/${rkey}` : ''; + + const [constellationurl] = useAtom(constellationURLAtom); + const infinitequeryresultsWithoutMedia = useInfiniteQuery({ + ...yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks( + { + constellation: constellationurl, + method: "/links", + target: atUri, + collection: "app.bsky.feed.post", + path: ".embed.record.uri", // embed.record.record.uri and embed.record.uri + } + ), + enabled: !!atUri, + }); + const infinitequeryresultsWithMedia = useInfiniteQuery({ + ...yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks( + { + constellation: constellationurl, + method: "/links", + target: atUri, + collection: "app.bsky.feed.post", + path: ".embed.record.record.uri", // embed.record.record.uri and embed.record.uri + } + ), + enabled: !!atUri, + }); + + const { + data: infiniteQuotesDataWithoutMedia, + fetchNextPage: fetchNextPageWithoutMedia, + hasNextPage: hasNextPageWithoutMedia, + isFetchingNextPage: isFetchingNextPageWithoutMedia, + isLoading: isLoadingWithoutMedia, + isError: isErrorWithoutMedia, + error: errorWithoutMedia, + } = infinitequeryresultsWithoutMedia; + const { + data: infiniteQuotesDataWithMedia, + fetchNextPage: fetchNextPageWithMedia, + hasNextPage: hasNextPageWithMedia, + isFetchingNextPage: isFetchingNextPageWithMedia, + isLoading: isLoadingWithMedia, + isError: isErrorWithMedia, + error: errorWithMedia, + } = infinitequeryresultsWithMedia; + + const fetchNextPage = async () => { + await Promise.all([ + hasNextPageWithMedia && fetchNextPageWithMedia(), + hasNextPageWithoutMedia && fetchNextPageWithoutMedia(), + ]); + }; + + const hasNextPage = hasNextPageWithMedia || hasNextPageWithoutMedia; + const isFetchingNextPage = isFetchingNextPageWithMedia || isFetchingNextPageWithoutMedia; + const isLoading = isLoadingWithMedia || isLoadingWithoutMedia; + + const allQuotes = React.useMemo(() => { + const withPages = infiniteQuotesDataWithMedia?.pages ?? []; + const withoutPages = infiniteQuotesDataWithoutMedia?.pages ?? []; + const maxLen = Math.max(withPages.length, withoutPages.length); + const merged: linksRecord[] = []; + + for (let i = 0; i < maxLen; i++) { + const a = withPages[i]?.linking_records ?? []; + const b = withoutPages[i]?.linking_records ?? []; + const mergedPage = [...a, ...b].sort((b, a) => a.rkey.localeCompare(b.rkey)); + merged.push(...mergedPage); + } + + return merged; + }, [infiniteQuotesDataWithMedia?.pages, infiniteQuotesDataWithoutMedia?.pages]); + + const quotesAturis = React.useMemo(() => { + return allQuotes.flatMap((r) => `at://${r.did}/${r.collection}/${r.rkey}`); + }, [allQuotes]); + + return ( + <> +
{ + if (window.history.length > 1) { + window.history.back(); + } else { + window.location.assign("/"); + } + }} + /> + + <> + {(() => { + if (isLoading) return ; + if (isErrorWithMedia) return ; + if (isErrorWithoutMedia) return ; + + if (!quotesAturis?.length) + return ; + })()} + + + {quotesAturis.map((m) => ( + + ))} + + {hasNextPage && ( + + )} + + ); +} diff --git a/src/routes/profile.$did/post.$rkey.reposted-by.tsx b/src/routes/profile.$did/post.$rkey.reposted-by.tsx new file mode 100644 --- /dev/null +++ b/src/routes/profile.$did/post.$rkey.reposted-by.tsx @@ -0,0 +1,100 @@ +import { useInfiniteQuery } from "@tanstack/react-query"; +import { createFileRoute } from "@tanstack/react-router"; +import { useAtom } from "jotai"; +import React from "react"; + +import { Header } from "~/components/Header"; +import { constellationURLAtom } from "~/utils/atoms"; +import { useQueryIdentity, yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks } from "~/utils/useQuery"; + +import { + EmptyState, + ErrorState, + LoadingState, + NotificationItem, +} from "../notifications"; + +export const Route = createFileRoute("/profile/$did/post/$rkey/reposted-by")({ + component: RouteComponent, +}); + +function RouteComponent() { + const { did, rkey } = Route.useParams(); + const { data: identity } = useQueryIdentity(did); + const atUri = identity?.did && rkey ? `at://${decodeURIComponent(identity.did)}/app.bsky.feed.post/${rkey}` : ''; + + const [constellationurl] = useAtom(constellationURLAtom); + const infinitequeryresults = useInfiniteQuery({ + ...yknowIReallyHateThisButWhateverGuardedConstructConstellationInfiniteQueryLinks( + { + constellation: constellationurl, + method: "/links", + target: atUri, + collection: "app.bsky.feed.repost", + path: ".subject.uri", + } + ), + enabled: !!atUri, + }); + + const { + data: infiniteRepostsData, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + isLoading, + isError, + error, + } = infinitequeryresults; + + const repostsAturis = React.useMemo(() => { + // Get all replies from the standard infinite query + return ( + infiniteRepostsData?.pages.flatMap( + (page) => + page?.linking_records.map( + (r) => `at://${r.did}/${r.collection}/${r.rkey}` + ) ?? [] + ) ?? [] + ); + }, [infiniteRepostsData]); + + return ( + <> +
{ + if (window.history.length > 1) { + window.history.back(); + } else { + window.location.assign("/"); + } + }} + /> + + <> + {(() => { + if (isLoading) return ; + if (isError) return ; + + if (!repostsAturis?.length) + return ; + })()} + + + {repostsAturis.map((m) => ( + + ))} + + {hasNextPage && ( + + )} + + ); +} diff --git a/src/routes/profile.$did/post.$rkey.tsx b/src/routes/profile.$did/post.$rkey.tsx --- a/src/routes/profile.$did/post.$rkey.tsx +++ b/src/routes/profile.$did/post.$rkey.tsx @@ -1,6 +1,6 @@ import { AtUri } from "@atproto/api"; import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query"; -import { createFileRoute, Outlet } from "@tanstack/react-router"; +import { createFileRoute, Outlet, useMatchRoute } from "@tanstack/react-router"; import { useAtom } from "jotai"; import React, { useLayoutEffect } from "react"; @@ -52,6 +52,9 @@ nopics?: boolean; lightboxCallback?: (d: LightboxProps) => void; }) { + const matchRoute = useMatchRoute() + const showMainPostRoute = !!matchRoute({ to: '/profile/$did/post/$rkey' }) || !!matchRoute({ to: '/profile/$did/post/$rkey/image/$i' }) + //const { get, set } = usePersistentStore(); const queryClient = useQueryClient(); // const [resolvedDid, setResolvedDid] = React.useState(null); @@ -190,19 +193,19 @@ data: identity, isLoading: isIdentityLoading, error: identityError, - } = useQueryIdentity(did); + } = useQueryIdentity(showMainPostRoute ? did : undefined); const resolvedDid = did.startsWith("did:") ? did : identity?.did; const atUri = React.useMemo( () => - resolvedDid + resolvedDid && showMainPostRoute ? `at://${decodeURIComponent(resolvedDid)}/app.bsky.feed.post/${rkey}` : undefined, - [resolvedDid, rkey] + [resolvedDid, rkey, showMainPostRoute] ); - const { data: mainPost } = useQueryPost(atUri); + const { data: mainPost } = useQueryPost(showMainPostRoute ? atUri : undefined); console.log("atUri",atUri) @@ -215,7 +218,7 @@ ); // @ts-expect-error i hate overloads - const { data: links } = useQueryConstellation(atUri?{ + const { data: links } = useQueryConstellation(atUri&&showMainPostRoute?{ method: "/links/all", target: atUri, } : { @@ -248,7 +251,7 @@ }, [links]); const { data: opreplies } = useQueryConstellation( - !!opdid && replyCount && replyCount >= 25 + showMainPostRoute && !!opdid && replyCount && replyCount >= 25 ? { method: "/links", target: atUri, @@ -289,7 +292,7 @@ path: ".reply.parent.uri", } ), - enabled: !!atUri, + enabled: !!atUri && showMainPostRoute, }); const { @@ -371,6 +374,7 @@ const [layoutReady, setLayoutReady] = React.useState(false); useLayoutEffect(() => { + if (!showMainPostRoute) return if (parents.length > 0 && !layoutReady && mainPostRef.current) { const mainPostElement = mainPostRef.current; @@ -389,13 +393,13 @@ // eslint-disable-next-line react-hooks/set-state-in-effect setLayoutReady(true); } - }, [parents, layoutReady]); + }, [parents, layoutReady, showMainPostRoute]); const [slingshoturl] = useAtom(slingshotURLAtom) React.useEffect(() => { - if (parentsLoading) { + if (parentsLoading || !showMainPostRoute) { setLayoutReady(false); } @@ -403,7 +407,7 @@ setLayoutReady(true); hasPerformedInitialLayout.current = true; } - }, [parentsLoading, mainPost]); + }, [parentsLoading, mainPost, showMainPostRoute]); React.useEffect(() => { if (!mainPost?.value?.reply?.parent?.uri) { @@ -444,103 +448,105 @@ return () => { ignore = true; }; - }, [mainPost, queryClient]); + }, [mainPost, queryClient, slingshoturl]); - if (!did || !rkey) return
Invalid post URI
; - if (isIdentityLoading) return
Resolving handle...
; - if (identityError) + if ((!did || !rkey) && showMainPostRoute) return
Invalid post URI
; + if (isIdentityLoading && showMainPostRoute) return
Resolving handle...
; + if (identityError && showMainPostRoute) return
{identityError.message}
; - if (!atUri) return
Could not construct post URI.
; + if (!atUri && showMainPostRoute) return
Could not construct post URI.
; return ( <> -
{ - if (window.history.length > 1) { - window.history.back(); - } else { - window.location.assign("/"); - } - }} - /> - - {parentsLoading && ( -
-
-
-
- Loading conversation... -
- )} - - {/* we should use the reply lines here thats provided by UPR*/} -
- {parents.map((parent, index) => ( - 0} - bottomReplyLine={true} - bottomBorder={false} - /> - ))} -
-
- 0} - nopics={!!nopics} - lightboxCallback={lightboxCallback} + {showMainPostRoute && (<> +
{ + if (window.history.length > 1) { + window.history.back(); + } else { + window.location.assign("/"); + } + }} /> -
-
+ + {parentsLoading && ( +
+
+
+
+ Loading conversation... +
+ )} + + {/* we should use the reply lines here thats provided by UPR*/} +
+ {parents.map((parent, index) => ( + 0} + bottomReplyLine={true} + bottomBorder={false} + /> + ))} +
+
+ 0} + nopics={!!nopics} + lightboxCallback={lightboxCallback} + /> +
- Replies +
+ Replies +
+
+ {replyAturis.length > 0 && + replyAturis.map((reply) => { + //const replyAtUri = `at://${reply.did}/app.bsky.feed.post/${reply.rkey}`; + return ( + + ); + })} + {hasNextPage && ( + + )} +
-
- {replyAturis.length > 0 && - replyAturis.map((reply) => { - //const replyAtUri = `at://${reply.did}/app.bsky.feed.post/${reply.rkey}`; - return ( - - ); - })} - {hasNextPage && ( - - )} -
-
+ )} ); } -- tangled.sh