+115
-21
src/components/UniversalPostRenderer.tsx
+115
-21
src/components/UniversalPostRenderer.tsx
···
1
-
import * as ATPAPI from "@atproto/api"
2
import { useNavigate } from "@tanstack/react-router";
3
import DOMPurify from "dompurify";
4
import { useAtom } from "jotai";
···
10
import {
11
composerAtom,
12
constellationURLAtom,
13
imgCDNAtom,
14
} from "~/utils/atoms";
15
import { useHydratedEmbed } from "~/utils/useHydrated";
···
162
isQuote,
163
filterNoReplies,
164
filterMustHaveMedia,
165
-
filterMustBeReply
166
}: UniversalPostRendererATURILoaderProps) {
167
// todo remove this once tree rendering is implemented, use a prop like isTree
168
const TEMPLINEAR = true;
···
526
? true
527
: maxReplies && !oldestOpsReplyElseNewestNonOpsReply
528
? false
529
-
: (maxReplies === 0 && (!replies || (!!replies && replies === 0))) ? false : bottomReplyLine
530
}
531
topReplyLine={topReplyLine}
532
//bottomBorder={maxReplies&&oldestOpsReplyElseNewestNonOpsReply ? false : bottomBorder}
···
553
filterMustBeReply={filterMustBeReply}
554
/>
555
<>
556
-
{(maxReplies && maxReplies === 0 && replies && replies > 0) ? (
557
<>
558
-
{/* <div>hello</div> */}
559
-
<MoreReplies atUri={atUri} />
560
</>
561
-
) : (<></>)}
562
</>
563
{!isQuote && oldestOpsReplyElseNewestNonOpsReply && (
564
<>
···
755
const hasImages = hasEmbed?.$type === "app.bsky.embed.images";
756
const hasVideo = hasEmbed?.$type === "app.bsky.embed.video";
757
const isquotewithmedia = hasEmbed?.$type === "app.bsky.embed.recordWithMedia";
758
-
const isQuotewithImages = isquotewithmedia && (hasEmbed as ATPAPI.AppBskyEmbedRecordWithMedia.Main)?.media?.$type === "app.bsky.embed.images";
759
-
const isQuotewithVideo = isquotewithmedia && (hasEmbed as ATPAPI.AppBskyEmbedRecordWithMedia.Main)?.media?.$type === "app.bsky.embed.video";
760
761
-
const hasMedia = hasEmbed && (hasImages || hasVideo || isQuotewithImages || isQuotewithVideo);
762
763
const {
764
data: hydratedEmbed,
···
854
// }, [fakepost, get, set]);
855
const thereply = (fakepost?.record as AppBskyFeedPost.Record)?.reply?.parent
856
?.uri;
857
-
const feedviewpostreplydid = thereply&&!filterNoReplies ? new AtUri(thereply).host : undefined;
858
const replyhookvalue = useQueryIdentity(
859
feedviewpost ? feedviewpostreplydid : undefined
860
);
···
1237
1238
import defaultpfp from "~/../public/favicon.png";
1239
import { useAuth } from "~/providers/UnifiedAuthProvider";
1240
-
import { FeedItemRenderAturiLoader, FollowButton, Mutual } from "~/routes/profile.$did";
1241
import type { LightboxProps } from "~/routes/profile.$did/post.$rkey.image.$i";
1242
import { useFastLike } from "~/utils/likeMutationQueue";
1243
// import type { OutputSchema } from "@atproto/api/dist/client/types/app/bsky/feed/getFeed";
···
1446
: undefined;
1447
1448
const emergencySalt = randomString();
1449
-
const fedi = (post.record as { bridgyOriginalText?: string })
1450
.bridgyOriginalText;
1451
1452
/* fuck you */
1453
const isMainItem = false;
···
1586
{post.author.displayName || post.author.handle}{" "}
1587
</div>
1588
<div className="text-gray-500 dark:text-gray-400 text-md flex flex-row gap-1">
1589
-
<Mutual targetdidorhandle={post.author.did} />@{post.author.handle}{" "}
1590
</div>
1591
</div>
1592
{uprrrsauthor?.description && (
···
1834
</div>
1835
</>
1836
)}
1837
-
<div style={{ paddingTop: post.embed && !concise && depth < 1 ? 4 : 0 }}>
1838
<>
1839
{expanded && (
1840
<div
···
2203
// <MaybeFeedCard view={embed.record} />
2204
// </div>
2205
// )
2206
-
} else if (!!reallybaduri && !!reallybadaturi && reallybadaturi.collection === "app.bsky.feed.generator") {
2207
-
return <div className="rounded-xl border"><FeedItemRenderAturiLoader aturi={reallybaduri} disableBottomBorder/></div>
2208
}
2209
2210
// list embed
···
2216
// <MaybeListCard view={embed.record} />
2217
// </div>
2218
// )
2219
-
} else if (!!reallybaduri && !!reallybadaturi && reallybadaturi.collection === "app.bsky.graph.list") {
2220
-
return <div className="rounded-xl border"><FeedItemRenderAturiLoader aturi={reallybaduri} disableBottomBorder listmode disablePropagation /></div>
2221
}
2222
2223
// starter pack embed
···
2229
// <StarterPackCard starterPack={embed.record} />
2230
// </div>
2231
// )
2232
-
} else if (!!reallybaduri && !!reallybadaturi && reallybadaturi.collection === "app.bsky.graph.starterpack") {
2233
-
return <div className="rounded-xl border"><FeedItemRenderAturiLoader aturi={reallybaduri} disableBottomBorder listmode disablePropagation /></div>
2234
}
2235
2236
// quote post
···
1
+
import * as ATPAPI from "@atproto/api";
2
import { useNavigate } from "@tanstack/react-router";
3
import DOMPurify from "dompurify";
4
import { useAtom } from "jotai";
···
10
import {
11
composerAtom,
12
constellationURLAtom,
13
+
enableBridgyTextAtom,
14
+
enableWafrnTextAtom,
15
imgCDNAtom,
16
} from "~/utils/atoms";
17
import { useHydratedEmbed } from "~/utils/useHydrated";
···
164
isQuote,
165
filterNoReplies,
166
filterMustHaveMedia,
167
+
filterMustBeReply,
168
}: UniversalPostRendererATURILoaderProps) {
169
// todo remove this once tree rendering is implemented, use a prop like isTree
170
const TEMPLINEAR = true;
···
528
? true
529
: maxReplies && !oldestOpsReplyElseNewestNonOpsReply
530
? false
531
+
: maxReplies === 0 && (!replies || (!!replies && replies === 0))
532
+
? false
533
+
: bottomReplyLine
534
}
535
topReplyLine={topReplyLine}
536
//bottomBorder={maxReplies&&oldestOpsReplyElseNewestNonOpsReply ? false : bottomBorder}
···
557
filterMustBeReply={filterMustBeReply}
558
/>
559
<>
560
+
{maxReplies && maxReplies === 0 && replies && replies > 0 ? (
561
<>
562
+
{/* <div>hello</div> */}
563
+
<MoreReplies atUri={atUri} />
564
</>
565
+
) : (
566
+
<></>
567
+
)}
568
</>
569
{!isQuote && oldestOpsReplyElseNewestNonOpsReply && (
570
<>
···
761
const hasImages = hasEmbed?.$type === "app.bsky.embed.images";
762
const hasVideo = hasEmbed?.$type === "app.bsky.embed.video";
763
const isquotewithmedia = hasEmbed?.$type === "app.bsky.embed.recordWithMedia";
764
+
const isQuotewithImages =
765
+
isquotewithmedia &&
766
+
(hasEmbed as ATPAPI.AppBskyEmbedRecordWithMedia.Main)?.media?.$type ===
767
+
"app.bsky.embed.images";
768
+
const isQuotewithVideo =
769
+
isquotewithmedia &&
770
+
(hasEmbed as ATPAPI.AppBskyEmbedRecordWithMedia.Main)?.media?.$type ===
771
+
"app.bsky.embed.video";
772
773
+
const hasMedia =
774
+
hasEmbed &&
775
+
(hasImages || hasVideo || isQuotewithImages || isQuotewithVideo);
776
777
const {
778
data: hydratedEmbed,
···
868
// }, [fakepost, get, set]);
869
const thereply = (fakepost?.record as AppBskyFeedPost.Record)?.reply?.parent
870
?.uri;
871
+
const feedviewpostreplydid =
872
+
thereply && !filterNoReplies ? new AtUri(thereply).host : undefined;
873
const replyhookvalue = useQueryIdentity(
874
feedviewpost ? feedviewpostreplydid : undefined
875
);
···
1252
1253
import defaultpfp from "~/../public/favicon.png";
1254
import { useAuth } from "~/providers/UnifiedAuthProvider";
1255
+
import {
1256
+
FeedItemRenderAturiLoader,
1257
+
FollowButton,
1258
+
Mutual,
1259
+
} from "~/routes/profile.$did";
1260
import type { LightboxProps } from "~/routes/profile.$did/post.$rkey.image.$i";
1261
import { useFastLike } from "~/utils/likeMutationQueue";
1262
// import type { OutputSchema } from "@atproto/api/dist/client/types/app/bsky/feed/getFeed";
···
1465
: undefined;
1466
1467
const emergencySalt = randomString();
1468
+
1469
+
const [showBridgyText] = useAtom(enableBridgyTextAtom);
1470
+
const [showWafrnText] = useAtom(enableWafrnTextAtom);
1471
+
1472
+
const unfedibridgy = (post.record as { bridgyOriginalText?: string })
1473
.bridgyOriginalText;
1474
+
const unfediwafrnPartial = (post.record as { fullText?: string }).fullText;
1475
+
const unfediwafrnTags = (post.record as { fullTags?: string }).fullTags;
1476
+
const unfediwafrnUnHost = (post.record as { fediverseId?: string })
1477
+
.fediverseId;
1478
+
1479
+
const undfediwafrnHost = unfediwafrnUnHost
1480
+
? new URL(unfediwafrnUnHost).hostname
1481
+
: undefined;
1482
+
1483
+
const tags = unfediwafrnTags
1484
+
? unfediwafrnTags
1485
+
.split("\n")
1486
+
.map((t) => t.trim())
1487
+
.filter(Boolean)
1488
+
: undefined;
1489
+
1490
+
const links = tags
1491
+
? tags
1492
+
.map((tag) => {
1493
+
const encoded = encodeURIComponent(tag);
1494
+
return `<a href="https://${undfediwafrnHost}/search/${encoded}" target="_blank">#${tag.replaceAll(' ','-')}</a>`;
1495
+
})
1496
+
.join("<br>")
1497
+
: "";
1498
+
1499
+
const unfediwafrn = unfediwafrnPartial
1500
+
? unfediwafrnPartial + (links ? `<br>${links}` : "")
1501
+
: undefined;
1502
+
1503
+
const fedi =
1504
+
(showBridgyText ? unfedibridgy : undefined) ??
1505
+
(showWafrnText ? unfediwafrn : undefined);
1506
1507
/* fuck you */
1508
const isMainItem = false;
···
1641
{post.author.displayName || post.author.handle}{" "}
1642
</div>
1643
<div className="text-gray-500 dark:text-gray-400 text-md flex flex-row gap-1">
1644
+
<Mutual targetdidorhandle={post.author.did} />@
1645
+
{post.author.handle}{" "}
1646
</div>
1647
</div>
1648
{uprrrsauthor?.description && (
···
1890
</div>
1891
</>
1892
)}
1893
+
<div
1894
+
style={{
1895
+
paddingTop: post.embed && !concise && depth < 1 ? 4 : 0,
1896
+
}}
1897
+
>
1898
<>
1899
{expanded && (
1900
<div
···
2263
// <MaybeFeedCard view={embed.record} />
2264
// </div>
2265
// )
2266
+
} else if (
2267
+
!!reallybaduri &&
2268
+
!!reallybadaturi &&
2269
+
reallybadaturi.collection === "app.bsky.feed.generator"
2270
+
) {
2271
+
return (
2272
+
<div className="rounded-xl border">
2273
+
<FeedItemRenderAturiLoader aturi={reallybaduri} disableBottomBorder />
2274
+
</div>
2275
+
);
2276
}
2277
2278
// list embed
···
2284
// <MaybeListCard view={embed.record} />
2285
// </div>
2286
// )
2287
+
} else if (
2288
+
!!reallybaduri &&
2289
+
!!reallybadaturi &&
2290
+
reallybadaturi.collection === "app.bsky.graph.list"
2291
+
) {
2292
+
return (
2293
+
<div className="rounded-xl border">
2294
+
<FeedItemRenderAturiLoader
2295
+
aturi={reallybaduri}
2296
+
disableBottomBorder
2297
+
listmode
2298
+
disablePropagation
2299
+
/>
2300
+
</div>
2301
+
);
2302
}
2303
2304
// starter pack embed
···
2310
// <StarterPackCard starterPack={embed.record} />
2311
// </div>
2312
// )
2313
+
} else if (
2314
+
!!reallybaduri &&
2315
+
!!reallybadaturi &&
2316
+
reallybadaturi.collection === "app.bsky.graph.starterpack"
2317
+
) {
2318
+
return (
2319
+
<div className="rounded-xl border">
2320
+
<FeedItemRenderAturiLoader
2321
+
aturi={reallybaduri}
2322
+
disableBottomBorder
2323
+
listmode
2324
+
disablePropagation
2325
+
/>
2326
+
</div>
2327
+
);
2328
}
2329
2330
// quote post
+30
-12
src/routes/settings.tsx
+30
-12
src/routes/settings.tsx
···
13
defaultslingshotURL,
14
defaultVideoCDN,
15
enableBitesAtom,
16
hueAtom,
17
imgCDNAtom,
18
slingshotURLAtom,
···
84
<SwitchSetting
85
atom={enableBitesAtom}
86
title={"Bites"}
87
-
description={"Enable Wafrn Bites to bite other people"}
88
//init={false}
89
/>
90
<p className="text-gray-500 dark:text-gray-400 py-4 px-4 text-sm border rounded-xl mx-4 mt-8 mb-4">
···
137
138
return (
139
<div className="flex items-center gap-4 px-4 ">
140
-
<div className="flex flex-col">
141
-
<label htmlFor="switch-demo" className="text-md">
142
-
{title}
143
-
</label>
144
-
<span className="text-sm text-gray-500 dark:text-gray-400">
145
-
{description}
146
-
</span>
147
-
</div>
148
-
149
-
<div className="flex-1" />
150
151
<Switch.Root
152
-
id="switch-demo"
153
checked={value}
154
onCheckedChange={(v) => setValue(v)}
155
className="m3switch root"
···
13
defaultslingshotURL,
14
defaultVideoCDN,
15
enableBitesAtom,
16
+
enableBridgyTextAtom,
17
+
enableWafrnTextAtom,
18
hueAtom,
19
imgCDNAtom,
20
slingshotURLAtom,
···
86
<SwitchSetting
87
atom={enableBitesAtom}
88
title={"Bites"}
89
+
description={"Enable Wafrn Bites to bite and be bitten by other people"}
90
+
//init={false}
91
+
/>
92
+
<div className="h-4" />
93
+
<SwitchSetting
94
+
atom={enableBridgyTextAtom}
95
+
title={"Bridgy Text"}
96
+
description={
97
+
"Show the original text of posts bridged from the Fediverse"
98
+
}
99
+
//init={false}
100
+
/>
101
+
<div className="h-4" />
102
+
<SwitchSetting
103
+
atom={enableWafrnTextAtom}
104
+
title={"Wafrn Text"}
105
+
description={
106
+
"Show the original text of posts from Wafrn instances"
107
+
}
108
//init={false}
109
/>
110
<p className="text-gray-500 dark:text-gray-400 py-4 px-4 text-sm border rounded-xl mx-4 mt-8 mb-4">
···
157
158
return (
159
<div className="flex items-center gap-4 px-4 ">
160
+
<label htmlFor={`switch-${title}`} className="flex flex-row flex-1">
161
+
<div className="flex flex-col">
162
+
<span className="text-md">{title}</span>
163
+
<span className="text-sm text-gray-500 dark:text-gray-400">
164
+
{description}
165
+
</span>
166
+
</div>
167
+
</label>
168
169
<Switch.Root
170
+
id={`switch-${title}`}
171
checked={value}
172
onCheckedChange={(v) => setValue(v)}
173
className="m3switch root"
+10
src/utils/atoms.ts
+10
src/utils/atoms.ts