an independent Bluesky client using Constellation, PDS Queries, and other services reddwarf.app
frontend spa bluesky reddwarf microcosm client app
99
fork

Configure Feed

Select the types of activity you want to include in your feed.

better experimental features, wafrn full text support

rimar1337 5252ff44 65f60fa0

+155 -33
+115 -21
src/components/UniversalPostRenderer.tsx
··· 1 - import * as ATPAPI from "@atproto/api" 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 + enableBridgyTextAtom, 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 - filterMustBeReply 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 - : (maxReplies === 0 && (!replies || (!!replies && replies === 0))) ? false : bottomReplyLine 531 + : maxReplies === 0 && (!replies || (!!replies && replies === 0)) 532 + ? false 533 + : bottomReplyLine 530 534 } 531 535 topReplyLine={topReplyLine} 532 536 //bottomBorder={maxReplies&&oldestOpsReplyElseNewestNonOpsReply ? false : bottomBorder} ··· 553 557 filterMustBeReply={filterMustBeReply} 554 558 /> 555 559 <> 556 - {(maxReplies && maxReplies === 0 && replies && replies > 0) ? ( 560 + {maxReplies && maxReplies === 0 && replies && replies > 0 ? ( 557 561 <> 558 - {/* <div>hello</div> */} 559 - <MoreReplies atUri={atUri} /> 562 + {/* <div>hello</div> */} 563 + <MoreReplies atUri={atUri} /> 560 564 </> 561 - ) : (<></>)} 565 + ) : ( 566 + <></> 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 - 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"; 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"; 760 772 761 - const hasMedia = hasEmbed && (hasImages || hasVideo || isQuotewithImages || isQuotewithVideo); 773 + const hasMedia = 774 + hasEmbed && 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 - const feedviewpostreplydid = thereply&&!filterNoReplies ? new AtUri(thereply).host : undefined; 871 + const feedviewpostreplydid = 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 - import { FeedItemRenderAturiLoader, FollowButton, Mutual } from "~/routes/profile.$did"; 1255 + import { 1256 + FeedItemRenderAturiLoader, 1257 + FollowButton, 1258 + Mutual, 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 - const fedi = (post.record as { bridgyOriginalText?: string }) 1468 + 1469 + const [showBridgyText] = useAtom(enableBridgyTextAtom); 1470 + const [showWafrnText] = useAtom(enableWafrnTextAtom); 1471 + 1472 + const unfedibridgy = (post.record as { bridgyOriginalText?: string }) 1450 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); 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 - <Mutual targetdidorhandle={post.author.did} />@{post.author.handle}{" "} 1644 + <Mutual targetdidorhandle={post.author.did} />@ 1645 + {post.author.handle}{" "} 1590 1646 </div> 1591 1647 </div> 1592 1648 {uprrrsauthor?.description && ( ··· 1834 1890 </div> 1835 1891 </> 1836 1892 )} 1837 - <div style={{ paddingTop: post.embed && !concise && depth < 1 ? 4 : 0 }}> 1893 + <div 1894 + style={{ 1895 + paddingTop: post.embed && !concise && depth < 1 ? 4 : 0, 1896 + }} 1897 + > 1838 1898 <> 1839 1899 {expanded && ( 1840 1900 <div ··· 2203 2263 // <MaybeFeedCard view={embed.record} /> 2204 2264 // </div> 2205 2265 // ) 2206 - } else if (!!reallybaduri && !!reallybadaturi && reallybadaturi.collection === "app.bsky.feed.generator") { 2207 - return <div className="rounded-xl border"><FeedItemRenderAturiLoader aturi={reallybaduri} disableBottomBorder/></div> 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 + ); 2208 2276 } 2209 2277 2210 2278 // list embed ··· 2216 2284 // <MaybeListCard view={embed.record} /> 2217 2285 // </div> 2218 2286 // ) 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> 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 + ); 2221 2302 } 2222 2303 2223 2304 // starter pack embed ··· 2229 2310 // <StarterPackCard starterPack={embed.record} /> 2230 2311 // </div> 2231 2312 // ) 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> 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 + ); 2234 2328 } 2235 2329 2236 2330 // quote post
+30 -12
src/routes/settings.tsx
··· 13 13 defaultslingshotURL, 14 14 defaultVideoCDN, 15 15 enableBitesAtom, 16 + enableBridgyTextAtom, 17 + enableWafrnTextAtom, 16 18 hueAtom, 17 19 imgCDNAtom, 18 20 slingshotURLAtom, ··· 84 86 <SwitchSetting 85 87 atom={enableBitesAtom} 86 88 title={"Bites"} 87 - description={"Enable Wafrn Bites to bite other people"} 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 + } 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 - <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" /> 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> 150 168 151 169 <Switch.Root 152 - id="switch-demo" 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 + 141 + export const enableBridgyTextAtom = atomWithStorage<boolean>( 142 + "enableBridgyTextAtom", 143 + false 144 + ); 145 + 146 + export const enableWafrnTextAtom = atomWithStorage<boolean>( 147 + "enableWafrnTextAtom", 148 + false 149 + );