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