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

Configure Feed

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

Polls: remove all unenforcable features from public polls

+96 -95
+12 -13
src/components/Composer.tsx
··· 111 111 c: pollData.c || undefined, 112 112 d: pollData.d || undefined, 113 113 expiry: pollData.expiry, 114 - multiple: false, 114 + multiple: true, 115 115 }); 116 116 117 117 if (dataUrl) { ··· 204 204 b: pollData.b, 205 205 c: pollData.c || undefined, 206 206 d: pollData.d || undefined, 207 - expiry: pollData.expiry.toISOString(), 208 - multiple: false, 207 + multiple: true, 209 208 createdAt: new Date().toISOString(), 210 209 }; 211 210 ··· 452 451 onChange((prev: any) => ({ ...prev, [field]: val })); 453 452 }; 454 453 455 - const handleDuration = (val: string) => { 456 - const hours = parseInt(val, 10); 457 - onChange((prev: any) => ({ 458 - ...prev, 459 - duration: val, 460 - expiry: addHours(new Date(), hours), 461 - })); 462 - }; 454 + // const handleDuration = (val: string) => { 455 + // const hours = parseInt(val, 10); 456 + // onChange((prev: any) => ({ 457 + // ...prev, 458 + // duration: val, 459 + // expiry: addHours(new Date(), hours), 460 + // })); 461 + // }; 463 462 464 463 return ( 465 464 <div className="mt-2 p-4 bg-gray-100 dark:bg-gray-900/50 rounded-xl border border-gray-200 dark:border-gray-800 space-y-3"> ··· 523 522 </label> 524 523 </div> 525 524 526 - <div className="flex flex-col gap-1 pt-2"> 525 + {/* <div className="flex flex-col gap-1 pt-2"> 527 526 <label className="text-xs font-semibold text-gray-500 uppercase tracking-wider pl-1"> 528 527 Poll Duration 529 528 </label> ··· 551 550 </svg> 552 551 </div> 553 552 </div> 554 - </div> 553 + </div> */} 555 554 </div> 556 555 ); 557 556 }
+4 -4
src/components/OGPoll.tsx
··· 10 10 b: string; 11 11 c?: string; 12 12 d?: string; 13 - expiry: Date; 13 + expiry?: Date; 14 14 } 15 15 16 16 export function RawOGC({ ··· 24 24 }: RawOGCProps) { 25 25 const options = [a, b, c, d].filter((opt): opt is string => !!opt); 26 26 27 - const formattedDate = expiry.toLocaleDateString('en-US', { 27 + const formattedDate = expiry?.toLocaleDateString('en-US', { 28 28 month: 'short', 29 29 day: 'numeric', 30 30 hour: 'numeric', ··· 46 46 47 47 {/* Multiplicity */} 48 48 <span className="text-2xl font-normal text-gray-300"> 49 - {multiple ? 'Select multiple options' : 'Select one option'} 49 + {multiple || !privateProviderHandle ? 'Select multiple options' : 'Select one option'} 50 50 </span> 51 51 </div> 52 52 ··· 67 67 {/* Expiry */} 68 68 <div className="flex items-center gap-3 rounded-xl bg-gray-800 px-6 py-3 font-medium text-gray-200"> 69 69 <IconMdiClockOutline /> 70 - <span>Expires {formattedDate}</span> 70 + {!formattedDate || !privateProviderHandle ? (<span>Never expires</span>) : (<span>Expires {formattedDate}</span>)} 71 71 </div> 72 72 73 73 {/* Branding */}
+80 -78
src/components/UniversalPostRenderer.tsx
··· 411 411 setReplies( 412 412 links 413 413 ? links?.links?.["app.bsky.feed.post"]?.[".reply.parent.uri"] 414 - ?.records || 0 414 + ?.records || 0 415 415 : null, 416 416 ); 417 417 }, [links]); ··· 459 459 460 460 const replyAturis = repliesData 461 461 ? repliesData.pages.flatMap((page) => 462 - page 463 - ? page.linking_records.map((record) => { 464 - const aturi = `at://${record.did}/${record.collection}/${record.rkey}`; 465 - return aturi; 466 - }) 467 - : [], 468 - ) 462 + page 463 + ? page.linking_records.map((record) => { 464 + const aturi = `at://${record.did}/${record.collection}/${record.rkey}`; 465 + return aturi; 466 + }) 467 + : [], 468 + ) 469 469 : []; 470 470 471 471 //const [oldestOpsReply, setOldestOpsReply] = useState<string | undefined>(undefined); ··· 625 625 opacity: 0.5, 626 626 }} 627 627 className="dark:bg-[repeating-linear-gradient(to_bottom,var(--color-gray-500)_0,var(--color-gray-400)_4px,transparent_4px,transparent_8px)]" 628 - //className="border-gray-400 dark:border-gray-500" 628 + //className="border-gray-400 dark:border-gray-500" 629 629 /> 630 630 </div> 631 631 ··· 771 771 const isQuotewithImages = 772 772 isquotewithmedia && 773 773 (hasEmbed as ATPAPI.AppBskyEmbedRecordWithMedia.Main)?.media?.$type === 774 - "app.bsky.embed.images"; 774 + "app.bsky.embed.images"; 775 775 const isQuotewithVideo = 776 776 isquotewithmedia && 777 777 (hasEmbed as ATPAPI.AppBskyEmbedRecordWithMedia.Main)?.media?.$type === 778 - "app.bsky.embed.video"; 778 + "app.bsky.embed.video"; 779 779 780 780 const hasMedia = 781 781 hasEmbed && ··· 1493 1493 1494 1494 const tags = unfediwafrnTags 1495 1495 ? unfediwafrnTags 1496 - .split("\n") 1497 - .map((t) => t.trim()) 1498 - .filter(Boolean) 1496 + .split("\n") 1497 + .map((t) => t.trim()) 1498 + .filter(Boolean) 1499 1499 : undefined; 1500 1500 1501 1501 const links = tags 1502 1502 ? tags 1503 - .map((tag) => { 1504 - const encoded = encodeURIComponent(tag); 1505 - return `<a href="https://${undfediwafrnHost}/search/${encoded}" target="_blank">#${tag.replaceAll(" ", "-")}</a>`; 1506 - }) 1507 - .join("<br>") 1503 + .map((tag) => { 1504 + const encoded = encodeURIComponent(tag); 1505 + return `<a href="https://${undfediwafrnHost}/search/${encoded}" target="_blank">#${tag.replaceAll(" ", "-")}</a>`; 1506 + }) 1507 + .join("<br>") 1508 1508 : ""; 1509 1509 1510 1510 const unfediwafrn = unfediwafrnPartial ··· 1517 1517 1518 1518 /* fuck you */ 1519 1519 const isMainItem = false; 1520 - const setMainItem = (any: any) => {}; 1520 + const setMainItem = (any: any) => { }; 1521 1521 // eslint-disable-next-line react-hooks/refs 1522 1522 //console.log("Received ref in UniversalPostRenderer:", usedref); 1523 1523 return ( ··· 1531 1531 : setMainItem 1532 1532 ? onPostClick 1533 1533 ? (e) => { 1534 - setMainItem({ post: post }); 1535 - onPostClick(e); 1536 - } 1534 + setMainItem({ post: post }); 1535 + onPostClick(e); 1536 + } 1537 1537 : () => { 1538 - setMainItem({ post: post }); 1539 - } 1538 + setMainItem({ post: post }); 1539 + } 1540 1540 : undefined 1541 1541 } 1542 1542 style={{ ··· 2019 2019 try { 2020 2020 await navigator.clipboard.writeText( 2021 2021 "https://bsky.app" + 2022 - "/profile/" + 2023 - post.author.handle + 2024 - "/post/" + 2025 - post.uri.split("/").pop(), 2022 + "/profile/" + 2023 + post.author.handle + 2024 + "/post/" + 2025 + post.uri.split("/").pop(), 2026 2026 ); 2027 2027 renderSnack({ 2028 2028 title: "Copied to clipboard!", ··· 2130 2130 | AppBskyEmbedVideo.View 2131 2131 | AppBskyEmbedExternal.View 2132 2132 | AppBskyEmbedRecordWithMedia.View 2133 - | { $type: string; [k: string]: unknown }; 2133 + | { $type: string;[k: string]: unknown }; 2134 2134 2135 2135 enum PostEmbedViewContext { 2136 2136 ThreadHighlighted = "ThreadHighlighted", ··· 2226 2226 const userVotesA = useGetOneToOneState( 2227 2227 agent?.did 2228 2228 ? { 2229 - target: pollUri, 2230 - user: agent?.did, 2231 - collection: "app.reddwarf.poll.vote.a", 2232 - path: ".subject.uri", 2233 - } 2229 + target: pollUri, 2230 + user: agent?.did, 2231 + collection: "app.reddwarf.poll.vote.a", 2232 + path: ".subject.uri", 2233 + } 2234 2234 : undefined, 2235 2235 ); 2236 2236 2237 2237 const userVotesB = useGetOneToOneState( 2238 2238 agent?.did 2239 2239 ? { 2240 - target: pollUri, 2241 - user: agent?.did, 2242 - collection: "app.reddwarf.poll.vote.b", 2243 - path: ".subject.uri", 2244 - } 2240 + target: pollUri, 2241 + user: agent?.did, 2242 + collection: "app.reddwarf.poll.vote.b", 2243 + path: ".subject.uri", 2244 + } 2245 2245 : undefined, 2246 2246 ); 2247 2247 2248 2248 const userVotesC = useGetOneToOneState( 2249 2249 agent?.did 2250 2250 ? { 2251 - target: pollUri, 2252 - user: agent?.did, 2253 - collection: "app.reddwarf.poll.vote.c", 2254 - path: ".subject.uri", 2255 - } 2251 + target: pollUri, 2252 + user: agent?.did, 2253 + collection: "app.reddwarf.poll.vote.c", 2254 + path: ".subject.uri", 2255 + } 2256 2256 : undefined, 2257 2257 ); 2258 2258 2259 2259 const userVotesD = useGetOneToOneState( 2260 2260 agent?.did 2261 2261 ? { 2262 - target: pollUri, 2263 - user: agent?.did, 2264 - collection: "app.reddwarf.poll.vote.d", 2265 - path: ".subject.uri", 2266 - } 2262 + target: pollUri, 2263 + user: agent?.did, 2264 + collection: "app.reddwarf.poll.vote.d", 2265 + path: ".subject.uri", 2266 + } 2267 2267 : undefined, 2268 2268 ); 2269 2269 ··· 2297 2297 }; 2298 2298 2299 2299 const options = [poll.a, poll.b, poll.c, poll.d].filter(Boolean); 2300 - const isExpired = poll.expiry ? new Date(poll.expiry) < new Date() : false; 2300 + const isExpired = false //poll.expiry ? new Date(poll.expiry) < new Date() : false; 2301 2301 2302 - const formattedDate = poll.expiry 2303 - ? new Date(poll.expiry).toLocaleDateString("en-US", { 2304 - month: "short", 2305 - day: "numeric", 2306 - hour: "numeric", 2307 - minute: "2-digit", 2308 - }) 2309 - : null; 2302 + // todo unused waiting for private polls 2303 + // undefined for public polls which equals never expires 2304 + const formattedDate = undefined; 2305 + // const formattedDate = poll.expiry 2306 + // ? new Date(poll.expiry).toLocaleDateString("en-US", { 2307 + // month: "short", 2308 + // day: "numeric", 2309 + // hour: "numeric", 2310 + // minute: "2-digit", 2311 + // }) 2312 + // : null; 2310 2313 2311 2314 // Calculate vote counts 2312 2315 const voteData = [ ··· 2467 2470 return ( 2468 2471 <div 2469 2472 key={index} 2470 - className={`group relative h-12 items-center justify-between rounded-lg border px-4 flex overflow-hidden ${ 2471 - !isExpired 2472 - ? hasVotedForOption 2473 - ? "bg-gray-100 dark:bg-gray-950 border-gray-200 dark:border-gray-700 hover:bg-gray-200 dark:hover:bg-gray-900 cursor-pointer outline-2 outline-gray-500 dark:outline-gray-400" 2474 - : "bg-gray-100 dark:bg-gray-950 border-gray-200 dark:border-gray-700 hover:bg-gray-200 dark:hover:bg-gray-900 cursor-pointer" 2475 - : "bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-700" 2476 - }`} 2473 + className={`group relative h-12 items-center justify-between rounded-lg border px-4 flex overflow-hidden ${!isExpired 2474 + ? hasVotedForOption 2475 + ? "bg-gray-100 dark:bg-gray-950 border-gray-200 dark:border-gray-700 hover:bg-gray-200 dark:hover:bg-gray-900 cursor-pointer outline-2 outline-gray-500 dark:outline-gray-400" 2476 + : "bg-gray-100 dark:bg-gray-950 border-gray-200 dark:border-gray-700 hover:bg-gray-200 dark:hover:bg-gray-900 cursor-pointer" 2477 + : "bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-700" 2478 + }`} 2477 2479 onClick={() => !isExpired && handleVote(optionKey)} 2478 2480 > 2479 2481 {/* Vote percentage bar - always show */} ··· 2504 2506 {/* Footer */} 2505 2507 <div className="mt-4 flex items-center justify-between text-sm text-gray-500 dark:text-gray-400"> 2506 2508 {/* Expiry */} 2507 - {formattedDate && !isExpired && ( 2508 - <div className="flex items-center gap-2"> 2509 - <IconMdiClockOutline /> 2509 + <div className="flex items-center gap-2"> 2510 + <IconMdiClockOutline /> 2511 + {/* <span>Expires {formattedDate}</span> */} 2512 + {formattedDate ? !isExpired ? ( 2510 2513 <span>Expires {formattedDate}</span> 2511 - </div> 2512 - )} 2514 + ) : (<span>Expired at {formattedDate}</span>) : <span>Never expires</span>} 2515 + </div> 2513 2516 2514 2517 {/* Status */} 2515 2518 <div className="flex items-center gap-2"> ··· 2846 2849 width: "100%", 2847 2850 aspectRatio: image.aspectRatio 2848 2851 ? (() => { 2849 - const { width, height } = image.aspectRatio; 2850 - const ratio = width / height; 2851 - return ratio < 0.5 ? "1 / 2" : `${width} / ${height}`; 2852 - })() 2852 + const { width, height } = image.aspectRatio; 2853 + const ratio = width / height; 2854 + return ratio < 0.5 ? "1 / 2" : `${width} / ${height}`; 2855 + })() 2853 2856 : "1 / 1", // fallback to square 2854 2857 //backgroundColor: theme.background, // fallback letterboxing color 2855 2858 borderRadius: 12, ··· 3547 3550 borderRadius: 12, 3548 3551 overflow: "hidden", 3549 3552 //border: `1px solid ${theme.border}`, 3550 - paddingTop: `${ 3551 - 100 / (aspect ? aspect.width / aspect.height : 16 / 9) 3552 - }%`, // 16:9 = 56.25%, 4:3 = 75% 3553 + paddingTop: `${100 / (aspect ? aspect.width / aspect.height : 16 / 9) 3554 + }%`, // 16:9 = 56.25%, 4:3 = 75% 3553 3555 }} 3554 3556 className="border border-gray-200 dark:border-gray-800 was7" 3555 3557 >