+6
-1
src/components/InfiniteCustomFeed.tsx
+6
-1
src/components/InfiniteCustomFeed.tsx
···
14
feedUri: string;
15
pdsUrl?: string;
16
feedServiceDid?: string;
17
}
18
19
export function InfiniteCustomFeed({
20
feedUri,
21
pdsUrl,
22
feedServiceDid,
23
}: InfiniteCustomFeedProps) {
24
const { agent } = useAuth();
25
-
const authed = !!agent?.did;
26
27
// const identityresultmaybe = useQueryIdentity(agent?.did);
28
// const identity = identityresultmaybe?.data;
···
45
isAuthed: authed ?? false,
46
pdsUrl: pdsUrl,
47
feedServiceDid: feedServiceDid,
48
});
49
const queryClient = useQueryClient();
50
···
14
feedUri: string;
15
pdsUrl?: string;
16
feedServiceDid?: string;
17
+
authedOverride?: boolean;
18
+
unauthedfeedurl?: string;
19
}
20
21
export function InfiniteCustomFeed({
22
feedUri,
23
pdsUrl,
24
feedServiceDid,
25
+
authedOverride,
26
+
unauthedfeedurl,
27
}: InfiniteCustomFeedProps) {
28
const { agent } = useAuth();
29
+
const authed = authedOverride || !!agent?.did;
30
31
// const identityresultmaybe = useQueryIdentity(agent?.did);
32
// const identity = identityresultmaybe?.data;
···
49
isAuthed: authed ?? false,
50
pdsUrl: pdsUrl,
51
feedServiceDid: feedServiceDid,
52
+
unauthedfeedurl: unauthedfeedurl,
53
});
54
const queryClient = useQueryClient();
55
+12
-1
src/components/UniversalPostRenderer.tsx
+12
-1
src/components/UniversalPostRenderer.tsx
···
1204
1205
import defaultpfp from "~/../public/favicon.png";
1206
import { useAuth } from "~/providers/UnifiedAuthProvider";
1207
-
import { FollowButton, Mutual } from "~/routes/profile.$did";
1208
import type { LightboxProps } from "~/routes/profile.$did/post.$rkey.image.$i";
1209
// import type { OutputSchema } from "@atproto/api/dist/client/types/app/bsky/feed/getFeed";
1210
// import type {
···
2179
}
2180
2181
if (AppBskyEmbedRecord.isView(embed)) {
2182
// custom feed embed (i.e. generator view)
2183
if (AppBskyFeedDefs.isGeneratorView(embed.record)) {
2184
// stopgap sorry
···
2188
// <MaybeFeedCard view={embed.record} />
2189
// </div>
2190
// )
2191
}
2192
2193
// list embed
···
2199
// <MaybeListCard view={embed.record} />
2200
// </div>
2201
// )
2202
}
2203
2204
// starter pack embed
···
2210
// <StarterPackCard starterPack={embed.record} />
2211
// </div>
2212
// )
2213
}
2214
2215
// quote post
···
2269
</div>
2270
);
2271
} else {
2272
return <>sorry</>;
2273
}
2274
//return <QuotePostRenderer record={embed.record} moderation={moderation} />;
···
1204
1205
import defaultpfp from "~/../public/favicon.png";
1206
import { useAuth } from "~/providers/UnifiedAuthProvider";
1207
+
import { FeedItemRenderAturiLoader, FollowButton, Mutual } from "~/routes/profile.$did";
1208
import type { LightboxProps } from "~/routes/profile.$did/post.$rkey.image.$i";
1209
// import type { OutputSchema } from "@atproto/api/dist/client/types/app/bsky/feed/getFeed";
1210
// import type {
···
2179
}
2180
2181
if (AppBskyEmbedRecord.isView(embed)) {
2182
+
// hey im really lazy and im gonna do it the bad way
2183
+
const reallybaduri = (embed?.record as any)?.uri as string | undefined;
2184
+
const reallybadaturi = reallybaduri ? new AtUri(reallybaduri) : undefined;
2185
+
2186
// custom feed embed (i.e. generator view)
2187
if (AppBskyFeedDefs.isGeneratorView(embed.record)) {
2188
// stopgap sorry
···
2192
// <MaybeFeedCard view={embed.record} />
2193
// </div>
2194
// )
2195
+
} else if (!!reallybaduri && !!reallybadaturi && reallybadaturi.collection === "app.bsky.feed.generator") {
2196
+
return <div className="rounded-xl border"><FeedItemRenderAturiLoader aturi={reallybaduri} disableBottomBorder/></div>
2197
}
2198
2199
// list embed
···
2205
// <MaybeListCard view={embed.record} />
2206
// </div>
2207
// )
2208
+
} else if (!!reallybaduri && !!reallybadaturi && reallybadaturi.collection === "app.bsky.graph.list") {
2209
+
return <div className="rounded-xl border"><FeedItemRenderAturiLoader aturi={reallybaduri} disableBottomBorder listmode /></div>
2210
}
2211
2212
// starter pack embed
···
2218
// <StarterPackCard starterPack={embed.record} />
2219
// </div>
2220
// )
2221
+
} else if (!!reallybaduri && !!reallybadaturi && reallybadaturi.collection === "app.bsky.graph.starterpack") {
2222
+
return <div className="rounded-xl border"><FeedItemRenderAturiLoader aturi={reallybaduri} disableBottomBorder listmode /></div>
2223
}
2224
2225
// quote post
···
2279
</div>
2280
);
2281
} else {
2282
+
console.log("what the hell is a ", embed);
2283
return <>sorry</>;
2284
}
2285
//return <QuotePostRenderer record={embed.record} moderation={moderation} />;
+21
src/routeTree.gen.ts
+21
src/routeTree.gen.ts
···
21
import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b'
22
import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a'
23
import { Route as ProfileDidPostRkeyRouteImport } from './routes/profile.$did/post.$rkey'
24
import { Route as ProfileDidPostRkeyRepostedByRouteImport } from './routes/profile.$did/post.$rkey.reposted-by'
25
import { Route as ProfileDidPostRkeyQuotesRouteImport } from './routes/profile.$did/post.$rkey.quotes'
26
import { Route as ProfileDidPostRkeyLikedByRouteImport } from './routes/profile.$did/post.$rkey.liked-by'
···
85
const ProfileDidPostRkeyRoute = ProfileDidPostRkeyRouteImport.update({
86
id: '/profile/$did/post/$rkey',
87
path: '/profile/$did/post/$rkey',
88
getParentRoute: () => rootRouteImport,
89
} as any)
90
const ProfileDidPostRkeyRepostedByRoute =
···
122
'/route-a': typeof PathlessLayoutNestedLayoutRouteARoute
123
'/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute
124
'/profile/$did': typeof ProfileDidIndexRoute
125
'/profile/$did/post/$rkey': typeof ProfileDidPostRkeyRouteWithChildren
126
'/profile/$did/post/$rkey/liked-by': typeof ProfileDidPostRkeyLikedByRoute
127
'/profile/$did/post/$rkey/quotes': typeof ProfileDidPostRkeyQuotesRoute
···
138
'/route-a': typeof PathlessLayoutNestedLayoutRouteARoute
139
'/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute
140
'/profile/$did': typeof ProfileDidIndexRoute
141
'/profile/$did/post/$rkey': typeof ProfileDidPostRkeyRouteWithChildren
142
'/profile/$did/post/$rkey/liked-by': typeof ProfileDidPostRkeyLikedByRoute
143
'/profile/$did/post/$rkey/quotes': typeof ProfileDidPostRkeyQuotesRoute
···
157
'/_pathlessLayout/_nested-layout/route-a': typeof PathlessLayoutNestedLayoutRouteARoute
158
'/_pathlessLayout/_nested-layout/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute
159
'/profile/$did/': typeof ProfileDidIndexRoute
160
'/profile/$did/post/$rkey': typeof ProfileDidPostRkeyRouteWithChildren
161
'/profile/$did/post/$rkey/liked-by': typeof ProfileDidPostRkeyLikedByRoute
162
'/profile/$did/post/$rkey/quotes': typeof ProfileDidPostRkeyQuotesRoute
···
175
| '/route-a'
176
| '/route-b'
177
| '/profile/$did'
178
| '/profile/$did/post/$rkey'
179
| '/profile/$did/post/$rkey/liked-by'
180
| '/profile/$did/post/$rkey/quotes'
···
191
| '/route-a'
192
| '/route-b'
193
| '/profile/$did'
194
| '/profile/$did/post/$rkey'
195
| '/profile/$did/post/$rkey/liked-by'
196
| '/profile/$did/post/$rkey/quotes'
···
209
| '/_pathlessLayout/_nested-layout/route-a'
210
| '/_pathlessLayout/_nested-layout/route-b'
211
| '/profile/$did/'
212
| '/profile/$did/post/$rkey'
213
| '/profile/$did/post/$rkey/liked-by'
214
| '/profile/$did/post/$rkey/quotes'
···
225
SettingsRoute: typeof SettingsRoute
226
CallbackIndexRoute: typeof CallbackIndexRoute
227
ProfileDidIndexRoute: typeof ProfileDidIndexRoute
228
ProfileDidPostRkeyRoute: typeof ProfileDidPostRkeyRouteWithChildren
229
}
230
···
314
preLoaderRoute: typeof ProfileDidPostRkeyRouteImport
315
parentRoute: typeof rootRouteImport
316
}
317
'/profile/$did/post/$rkey/reposted-by': {
318
id: '/profile/$did/post/$rkey/reposted-by'
319
path: '/reposted-by'
···
401
SettingsRoute: SettingsRoute,
402
CallbackIndexRoute: CallbackIndexRoute,
403
ProfileDidIndexRoute: ProfileDidIndexRoute,
404
ProfileDidPostRkeyRoute: ProfileDidPostRkeyRouteWithChildren,
405
}
406
export const routeTree = rootRouteImport
···
21
import { Route as PathlessLayoutNestedLayoutRouteBRouteImport } from './routes/_pathlessLayout/_nested-layout/route-b'
22
import { Route as PathlessLayoutNestedLayoutRouteARouteImport } from './routes/_pathlessLayout/_nested-layout/route-a'
23
import { Route as ProfileDidPostRkeyRouteImport } from './routes/profile.$did/post.$rkey'
24
+
import { Route as ProfileDidFeedRkeyRouteImport } from './routes/profile.$did/feed.$rkey'
25
import { Route as ProfileDidPostRkeyRepostedByRouteImport } from './routes/profile.$did/post.$rkey.reposted-by'
26
import { Route as ProfileDidPostRkeyQuotesRouteImport } from './routes/profile.$did/post.$rkey.quotes'
27
import { Route as ProfileDidPostRkeyLikedByRouteImport } from './routes/profile.$did/post.$rkey.liked-by'
···
86
const ProfileDidPostRkeyRoute = ProfileDidPostRkeyRouteImport.update({
87
id: '/profile/$did/post/$rkey',
88
path: '/profile/$did/post/$rkey',
89
+
getParentRoute: () => rootRouteImport,
90
+
} as any)
91
+
const ProfileDidFeedRkeyRoute = ProfileDidFeedRkeyRouteImport.update({
92
+
id: '/profile/$did/feed/$rkey',
93
+
path: '/profile/$did/feed/$rkey',
94
getParentRoute: () => rootRouteImport,
95
} as any)
96
const ProfileDidPostRkeyRepostedByRoute =
···
128
'/route-a': typeof PathlessLayoutNestedLayoutRouteARoute
129
'/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute
130
'/profile/$did': typeof ProfileDidIndexRoute
131
+
'/profile/$did/feed/$rkey': typeof ProfileDidFeedRkeyRoute
132
'/profile/$did/post/$rkey': typeof ProfileDidPostRkeyRouteWithChildren
133
'/profile/$did/post/$rkey/liked-by': typeof ProfileDidPostRkeyLikedByRoute
134
'/profile/$did/post/$rkey/quotes': typeof ProfileDidPostRkeyQuotesRoute
···
145
'/route-a': typeof PathlessLayoutNestedLayoutRouteARoute
146
'/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute
147
'/profile/$did': typeof ProfileDidIndexRoute
148
+
'/profile/$did/feed/$rkey': typeof ProfileDidFeedRkeyRoute
149
'/profile/$did/post/$rkey': typeof ProfileDidPostRkeyRouteWithChildren
150
'/profile/$did/post/$rkey/liked-by': typeof ProfileDidPostRkeyLikedByRoute
151
'/profile/$did/post/$rkey/quotes': typeof ProfileDidPostRkeyQuotesRoute
···
165
'/_pathlessLayout/_nested-layout/route-a': typeof PathlessLayoutNestedLayoutRouteARoute
166
'/_pathlessLayout/_nested-layout/route-b': typeof PathlessLayoutNestedLayoutRouteBRoute
167
'/profile/$did/': typeof ProfileDidIndexRoute
168
+
'/profile/$did/feed/$rkey': typeof ProfileDidFeedRkeyRoute
169
'/profile/$did/post/$rkey': typeof ProfileDidPostRkeyRouteWithChildren
170
'/profile/$did/post/$rkey/liked-by': typeof ProfileDidPostRkeyLikedByRoute
171
'/profile/$did/post/$rkey/quotes': typeof ProfileDidPostRkeyQuotesRoute
···
184
| '/route-a'
185
| '/route-b'
186
| '/profile/$did'
187
+
| '/profile/$did/feed/$rkey'
188
| '/profile/$did/post/$rkey'
189
| '/profile/$did/post/$rkey/liked-by'
190
| '/profile/$did/post/$rkey/quotes'
···
201
| '/route-a'
202
| '/route-b'
203
| '/profile/$did'
204
+
| '/profile/$did/feed/$rkey'
205
| '/profile/$did/post/$rkey'
206
| '/profile/$did/post/$rkey/liked-by'
207
| '/profile/$did/post/$rkey/quotes'
···
220
| '/_pathlessLayout/_nested-layout/route-a'
221
| '/_pathlessLayout/_nested-layout/route-b'
222
| '/profile/$did/'
223
+
| '/profile/$did/feed/$rkey'
224
| '/profile/$did/post/$rkey'
225
| '/profile/$did/post/$rkey/liked-by'
226
| '/profile/$did/post/$rkey/quotes'
···
237
SettingsRoute: typeof SettingsRoute
238
CallbackIndexRoute: typeof CallbackIndexRoute
239
ProfileDidIndexRoute: typeof ProfileDidIndexRoute
240
+
ProfileDidFeedRkeyRoute: typeof ProfileDidFeedRkeyRoute
241
ProfileDidPostRkeyRoute: typeof ProfileDidPostRkeyRouteWithChildren
242
}
243
···
327
preLoaderRoute: typeof ProfileDidPostRkeyRouteImport
328
parentRoute: typeof rootRouteImport
329
}
330
+
'/profile/$did/feed/$rkey': {
331
+
id: '/profile/$did/feed/$rkey'
332
+
path: '/profile/$did/feed/$rkey'
333
+
fullPath: '/profile/$did/feed/$rkey'
334
+
preLoaderRoute: typeof ProfileDidFeedRkeyRouteImport
335
+
parentRoute: typeof rootRouteImport
336
+
}
337
'/profile/$did/post/$rkey/reposted-by': {
338
id: '/profile/$did/post/$rkey/reposted-by'
339
path: '/reposted-by'
···
421
SettingsRoute: SettingsRoute,
422
CallbackIndexRoute: CallbackIndexRoute,
423
ProfileDidIndexRoute: ProfileDidIndexRoute,
424
+
ProfileDidFeedRkeyRoute: ProfileDidFeedRkeyRoute,
425
ProfileDidPostRkeyRoute: ProfileDidPostRkeyRouteWithChildren,
426
}
427
export const routeTree = rootRouteImport
+90
src/routes/profile.$did/feed.$rkey.tsx
+90
src/routes/profile.$did/feed.$rkey.tsx
···
···
1
+
import * as ATPAPI from "@atproto/api";
2
+
import { AtUri } from "@atproto/api";
3
+
import { createFileRoute } from "@tanstack/react-router";
4
+
import { useAtom } from "jotai";
5
+
6
+
import { Header } from "~/components/Header";
7
+
import { InfiniteCustomFeed } from "~/components/InfiniteCustomFeed";
8
+
import { useAuth } from "~/providers/UnifiedAuthProvider";
9
+
import { quickAuthAtom } from "~/utils/atoms";
10
+
import { useQueryArbitrary, useQueryIdentity } from "~/utils/useQuery";
11
+
12
+
export const Route = createFileRoute("/profile/$did/feed/$rkey")({
13
+
component: FeedRoute,
14
+
});
15
+
16
+
function FeedRoute() {
17
+
const { did, rkey } = Route.useParams();
18
+
const { agent, status } = useAuth();
19
+
const { data: identitydata } = useQueryIdentity(did);
20
+
const { data: identity } = useQueryIdentity(agent?.did);
21
+
const uri = `at://${identitydata?.did || did}/app.bsky.feed.generator/${rkey}`;
22
+
const aturi = new AtUri(uri);
23
+
const { data: feeddata } = useQueryArbitrary(uri);
24
+
25
+
const [quickAuth, setQuickAuth] = useAtom(quickAuthAtom);
26
+
const isAuthRestoring = quickAuth ? status === "loading" : false;
27
+
28
+
const authed = status === "signedIn";
29
+
30
+
const feedServiceDid = !isAuthRestoring
31
+
? ((feeddata?.value as any)?.did as string | undefined)
32
+
: undefined;
33
+
34
+
// const {
35
+
// data: feedData,
36
+
// isLoading: isFeedLoading,
37
+
// error: feedError,
38
+
// } = useQueryFeedSkeleton({
39
+
// feedUri: selectedFeed!,
40
+
// agent: agent ?? undefined,
41
+
// isAuthed: authed ?? false,
42
+
// pdsUrl: identity?.pds,
43
+
// feedServiceDid: feedServiceDid,
44
+
// });
45
+
46
+
// const feed = feedData?.feed || [];
47
+
48
+
const isReadyForAuthedFeed =
49
+
!isAuthRestoring && authed && agent && identity?.pds && feedServiceDid;
50
+
const isReadyForUnauthedFeed = !isAuthRestoring && !authed;
51
+
52
+
const feed: ATPAPI.AppBskyFeedGenerator.Record | undefined = feeddata?.value;
53
+
54
+
const web = feedServiceDid?.replace(/^did:web:/, "") || "";
55
+
56
+
return (
57
+
<>
58
+
<Header
59
+
title={feed?.displayName || aturi.rkey}
60
+
backButtonCallback={() => {
61
+
if (window.history.length > 1) {
62
+
window.history.back();
63
+
} else {
64
+
window.location.assign("/");
65
+
}
66
+
}}
67
+
/>
68
+
69
+
{isAuthRestoring ||
70
+
(authed && (!identity?.pds || !feedServiceDid) && (
71
+
<div className="p-4 text-center text-gray-500">
72
+
Preparing your feed...
73
+
</div>
74
+
))}
75
+
76
+
{!isAuthRestoring && (isReadyForAuthedFeed || isReadyForUnauthedFeed) ? (
77
+
<InfiniteCustomFeed
78
+
key={uri}
79
+
feedUri={uri}
80
+
pdsUrl={identity?.pds}
81
+
feedServiceDid={feedServiceDid}
82
+
authedOverride={true}
83
+
unauthedfeedurl={web}
84
+
/>
85
+
) : (
86
+
<div className="p-4 text-center text-gray-500">Loading.......</div>
87
+
)}
88
+
</>
89
+
);
90
+
}
+63
-19
src/routes/profile.$did/index.tsx
+63
-19
src/routes/profile.$did/index.tsx
···
1
import { RichText } from "@atproto/api";
2
import * as ATPAPI from "@atproto/api";
3
import { useQueryClient } from "@tanstack/react-query";
4
-
import { createFileRoute, useNavigate } from "@tanstack/react-router";
5
import { useAtom } from "jotai";
6
import React, { type ReactNode, useEffect, useState } from "react";
7
···
24
} from "~/utils/followState";
25
import {
26
useInfiniteQueryAuthorFeed,
27
useQueryConstellation,
28
useQueryIdentity,
29
useQueryProfile,
···
403
);
404
}
405
406
-
function FeedItemRender({
407
feed,
408
-
listmode
409
}: {
410
-
feed: { uri: string; cid: string; value: ATPAPI.AppBskyFeedGenerator.Record };
411
listmode?: boolean;
412
}) {
413
-
const name = listmode ? feed.value?.name as string : feed.value?.displayName as string;
414
const aturi = new ATPAPI.AtUri(feed.uri);
415
-
const {data: identity} = useQueryIdentity(aturi.host);
416
const resolvedDid = identity?.did;
417
const [imgcdn] = useAtom(imgCDNAtom);
418
···
422
return `https://${imgcdn}/img/avatar/plain/${resolvedDid}/${link}@jpeg`;
423
}
424
425
// @ts-expect-error overloads sucks
426
-
const {data: likes} = useQueryConstellation(!listmode ? {
427
-
target: feed.uri,
428
-
method: "/links/count",
429
-
collection: "app.bsky.feed.like",
430
-
path: ".subject.uri"
431
-
} : undefined)
432
433
return (
434
-
<div className="px-4 py-4 border-b flex flex-col gap-1">
435
<div className="flex flex-row gap-3">
436
<div className="min-w-10 min-h-10">
437
-
<img src={getAvatarThumbnailUrl(feed) || defaultpfp} className="h-10 w-10 rounded border" />
438
</div>
439
<div className="flex flex-col">
440
<span className="">{name}</span>
441
-
<span className=" text-sm px-1.5 py-0.5 text-gray-500 bg-gray-200 dark:text-gray-400 dark:bg-gray-800 rounded-lg flex flex-row items-center justify-center">{feed.value.did || aturi.rkey}</span>
442
</div>
443
<div className="flex-1" />
444
{/* <div className="button bg-red-500 rounded-full min-w-[60px]" /> */}
445
</div>
446
<span className=" text-sm">{feed.value?.description}</span>
447
-
{!listmode && (<span className=" text-sm dark:text-gray-400 text-gray-500">Liked by {(likes as unknown as any)?.total as number || 0} users</span>)}
448
-
</div>
449
);
450
}
451
-
452
453
function ListsTab({ did }: { did: string }) {
454
useReusableTabScrollRestore(`Profile` + did);
···
487
if (!feed || !feed?.value) return;
488
const feedGenRecord =
489
feed.value as unknown as ATPAPI.AppBskyFeedGenerator.Record;
490
-
return <FeedItemRender listmode={true} feed={feed as any} key={feed.uri} />;
491
})}
492
</div>
493
···
1
import { RichText } from "@atproto/api";
2
import * as ATPAPI from "@atproto/api";
3
import { useQueryClient } from "@tanstack/react-query";
4
+
import { createFileRoute, Link, useNavigate } from "@tanstack/react-router";
5
import { useAtom } from "jotai";
6
import React, { type ReactNode, useEffect, useState } from "react";
7
···
24
} from "~/utils/followState";
25
import {
26
useInfiniteQueryAuthorFeed,
27
+
useQueryArbitrary,
28
useQueryConstellation,
29
useQueryIdentity,
30
useQueryProfile,
···
404
);
405
}
406
407
+
export function FeedItemRenderAturiLoader({
408
+
aturi,
409
+
listmode,
410
+
disableBottomBorder,
411
+
}: {
412
+
aturi: string;
413
+
listmode?: boolean;
414
+
disableBottomBorder?: boolean;
415
+
}) {
416
+
const { data: record } = useQueryArbitrary(aturi);
417
+
418
+
if (!record) return;
419
+
return (
420
+
<FeedItemRender
421
+
listmode={listmode}
422
+
feed={record}
423
+
disableBottomBorder={disableBottomBorder}
424
+
/>
425
+
);
426
+
}
427
+
428
+
export function FeedItemRender({
429
feed,
430
+
listmode,
431
+
disableBottomBorder,
432
}: {
433
+
feed: { uri: string; cid: string; value: any };
434
listmode?: boolean;
435
+
disableBottomBorder?: boolean;
436
}) {
437
+
const name = listmode
438
+
? (feed.value?.name as string)
439
+
: (feed.value?.displayName as string);
440
const aturi = new ATPAPI.AtUri(feed.uri);
441
+
const { data: identity } = useQueryIdentity(aturi.host);
442
const resolvedDid = identity?.did;
443
const [imgcdn] = useAtom(imgCDNAtom);
444
···
448
return `https://${imgcdn}/img/avatar/plain/${resolvedDid}/${link}@jpeg`;
449
}
450
451
+
const { data: likes } = useQueryConstellation(
452
// @ts-expect-error overloads sucks
453
+
!listmode
454
+
? {
455
+
target: feed.uri,
456
+
method: "/links/count",
457
+
collection: "app.bsky.feed.like",
458
+
path: ".subject.uri",
459
+
}
460
+
: undefined
461
+
);
462
463
return (
464
+
<Link
465
+
className={`px-4 py-4 ${!disableBottomBorder && "border-b"} flex flex-col gap-1`}
466
+
to="/profile/$did/feed/$rkey"
467
+
params={{ did: aturi.host, rkey: aturi.rkey }}
468
+
>
469
<div className="flex flex-row gap-3">
470
<div className="min-w-10 min-h-10">
471
+
<img
472
+
src={getAvatarThumbnailUrl(feed) || defaultpfp}
473
+
className="h-10 w-10 rounded border"
474
+
/>
475
</div>
476
<div className="flex flex-col">
477
<span className="">{name}</span>
478
+
<span className=" text-sm px-1.5 py-0.5 text-gray-500 bg-gray-200 dark:text-gray-400 dark:bg-gray-800 rounded-lg flex flex-row items-center justify-center">
479
+
{feed.value.did || aturi.rkey}
480
+
</span>
481
</div>
482
<div className="flex-1" />
483
{/* <div className="button bg-red-500 rounded-full min-w-[60px]" /> */}
484
</div>
485
<span className=" text-sm">{feed.value?.description}</span>
486
+
{!listmode && (
487
+
<span className=" text-sm dark:text-gray-400 text-gray-500">
488
+
Liked by {((likes as unknown as any)?.total as number) || 0} users
489
+
</span>
490
+
)}
491
+
</Link>
492
);
493
}
494
495
function ListsTab({ did }: { did: string }) {
496
useReusableTabScrollRestore(`Profile` + did);
···
529
if (!feed || !feed?.value) return;
530
const feedGenRecord =
531
feed.value as unknown as ATPAPI.AppBskyFeedGenerator.Record;
532
+
return (
533
+
<FeedItemRender listmode={true} feed={feed as any} key={feed.uri} />
534
+
);
535
})}
536
</div>
537
+7
-4
src/utils/useQuery.ts
+7
-4
src/utils/useQuery.ts
···
573
isAuthed: boolean;
574
pdsUrl?: string;
575
feedServiceDid?: string;
576
}) {
577
-
const { feedUri, agent, isAuthed, pdsUrl, feedServiceDid } = options;
578
579
return queryOptions({
580
queryKey: ["feedSkeleton", feedUri, { isAuthed, did: agent?.did }],
···
582
queryFn: async ({ pageParam }: QueryFunctionContext): Promise<FeedSkeletonPage> => {
583
const cursorParam = pageParam ? `&cursor=${pageParam}` : "";
584
585
-
if (isAuthed) {
586
if (!agent || !pdsUrl || !feedServiceDid) {
587
throw new Error("Missing required info for authenticated feed fetch.");
588
}
···
597
if (!res.ok) throw new Error(`Authenticated feed fetch failed: ${res.statusText}`);
598
return (await res.json()) as FeedSkeletonPage;
599
} else {
600
-
const url = `https://discover.bsky.app/xrpc/app.bsky.feed.getFeedSkeleton?feed=${encodeURIComponent(feedUri)}${cursorParam}`;
601
const res = await fetch(url);
602
if (!res.ok) throw new Error(`Public feed fetch failed: ${res.statusText}`);
603
return (await res.json()) as FeedSkeletonPage;
···
612
isAuthed: boolean;
613
pdsUrl?: string;
614
feedServiceDid?: string;
615
}) {
616
const { queryKey, queryFn } = constructInfiniteFeedSkeletonQuery(options);
617
···
622
getNextPageParam: (lastPage) => lastPage.cursor as null | undefined,
623
staleTime: Infinity,
624
refetchOnWindowFocus: false,
625
-
enabled: !!options.feedUri && (options.isAuthed ? !!options.agent && !!options.pdsUrl && !!options.feedServiceDid : true),
626
}), queryKey: queryKey};
627
}
628
···
573
isAuthed: boolean;
574
pdsUrl?: string;
575
feedServiceDid?: string;
576
+
// todo the hell is a unauthedfeedurl
577
+
unauthedfeedurl?: string;
578
}) {
579
+
const { feedUri, agent, isAuthed, pdsUrl, feedServiceDid, unauthedfeedurl } = options;
580
581
return queryOptions({
582
queryKey: ["feedSkeleton", feedUri, { isAuthed, did: agent?.did }],
···
584
queryFn: async ({ pageParam }: QueryFunctionContext): Promise<FeedSkeletonPage> => {
585
const cursorParam = pageParam ? `&cursor=${pageParam}` : "";
586
587
+
if (isAuthed && !unauthedfeedurl) {
588
if (!agent || !pdsUrl || !feedServiceDid) {
589
throw new Error("Missing required info for authenticated feed fetch.");
590
}
···
599
if (!res.ok) throw new Error(`Authenticated feed fetch failed: ${res.statusText}`);
600
return (await res.json()) as FeedSkeletonPage;
601
} else {
602
+
const url = `https://${unauthedfeedurl ? unauthedfeedurl : "discover.bsky.app"}/xrpc/app.bsky.feed.getFeedSkeleton?feed=${encodeURIComponent(feedUri)}${cursorParam}`;
603
const res = await fetch(url);
604
if (!res.ok) throw new Error(`Public feed fetch failed: ${res.statusText}`);
605
return (await res.json()) as FeedSkeletonPage;
···
614
isAuthed: boolean;
615
pdsUrl?: string;
616
feedServiceDid?: string;
617
+
unauthedfeedurl?: string;
618
}) {
619
const { queryKey, queryFn } = constructInfiniteFeedSkeletonQuery(options);
620
···
625
getNextPageParam: (lastPage) => lastPage.cursor as null | undefined,
626
staleTime: Infinity,
627
refetchOnWindowFocus: false,
628
+
enabled: !!options.feedUri && (options.isAuthed ? (!!options.agent && !!options.pdsUrl || !!options.unauthedfeedurl) && !!options.feedServiceDid : true),
629
}), queryKey: queryKey};
630
}
631