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

Configure Feed

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

clickable profile hover card

rimar1337 96fdf44f b3faaa61

+61 -41
+1
src/components/UniversalPostRenderer.tsx
··· 1542 1542 className="rounded-md p-4 w-72 bg-gray-50 dark:bg-gray-900 shadow-lg border border-gray-300 dark:border-gray-800 animate-slide-fade z-50" 1543 1543 side={"bottom"} 1544 1544 sideOffset={5} 1545 + onClick={onProfileClick} 1545 1546 > 1546 1547 <div className="flex flex-col gap-2"> 1547 1548 <div className="flex flex-row">
+60 -41
src/routes/profile.$did/index.tsx
··· 2 2 import { useQueryClient } from "@tanstack/react-query"; 3 3 import { createFileRoute, useNavigate } from "@tanstack/react-router"; 4 4 import { useAtom } from "jotai"; 5 - import React, { type ReactNode,useEffect, useState } from "react"; 5 + import React, { type ReactNode, useEffect, useState } from "react"; 6 6 7 7 import { Header } from "~/components/Header"; 8 - import { renderTextWithFacets, UniversalPostRendererATURILoader } from "~/components/UniversalPostRenderer"; 8 + import { 9 + renderTextWithFacets, 10 + UniversalPostRendererATURILoader, 11 + } from "~/components/UniversalPostRenderer"; 9 12 import { useAuth } from "~/providers/UnifiedAuthProvider"; 10 13 import { imgCDNAtom } from "~/utils/atoms"; 11 - import { toggleFollow, useGetFollowState, useGetOneToOneState } from "~/utils/followState"; 14 + import { 15 + toggleFollow, 16 + useGetFollowState, 17 + useGetOneToOneState, 18 + } from "~/utils/followState"; 12 19 import { 13 20 useInfiniteQueryAuthorFeed, 14 21 useQueryIdentity, ··· 172 179 <div className="mt-16 pb-2 px-4 text-gray-900 dark:text-gray-100"> 173 180 <div className="font-bold text-2xl">{displayName}</div> 174 181 <div className="text-gray-500 dark:text-gray-400 text-base mb-3 flex flex-row gap-1"> 175 - <Mutual targetdidorhandle={did} /> 182 + <Mutual targetdidorhandle={did} /> 176 183 {handle} 177 184 </div> 178 185 {description && ( 179 186 <div className="text-base leading-relaxed text-gray-800 dark:text-gray-300 mb-5 whitespace-pre-wrap break-words text-[15px]"> 180 187 {/* {description} */} 181 - <RichTextRenderer key={did} description={description}/> 188 + <RichTextRenderer key={did} description={description} /> 182 189 </div> 183 190 )} 184 191 </div> ··· 222 229 ); 223 230 } 224 231 225 - export function FollowButton({targetdidorhandle}:{targetdidorhandle: string}) { 226 - const {agent} = useAuth() 227 - const {data: identity} = useQueryIdentity(targetdidorhandle); 232 + export function FollowButton({ 233 + targetdidorhandle, 234 + }: { 235 + targetdidorhandle: string; 236 + }) { 237 + const { agent } = useAuth(); 238 + const { data: identity } = useQueryIdentity(targetdidorhandle); 228 239 const queryClient = useQueryClient(); 229 240 230 241 const followRecords = useGetFollowState({ 231 242 target: identity?.did ?? targetdidorhandle, 232 243 user: agent?.did, 233 244 }); 234 - 245 + 235 246 return ( 236 247 <> 237 248 {identity?.did !== agent?.did ? ( 238 249 <> 239 250 {!(followRecords?.length && followRecords?.length > 0) ? ( 240 251 <button 241 - onClick={(e) => 242 - { 252 + onClick={(e) => { 243 253 e.stopPropagation(); 244 254 toggleFollow({ 245 255 agent: agent || undefined, 246 256 targetDid: identity?.did, 247 257 followRecords: followRecords, 248 258 queryClient: queryClient, 249 - }) 250 - } 251 - } 259 + }); 260 + }} 252 261 className="rounded-full h-10 bg-gray-300 dark:bg-gray-600 hover:bg-gray-400 dark:hover:bg-gray-500 transition-colors px-4 py-2 text-[14px]" 253 262 > 254 263 Follow 255 264 </button> 256 265 ) : ( 257 266 <button 258 - onClick={(e) => 259 - { 267 + onClick={(e) => { 260 268 e.stopPropagation(); 261 269 toggleFollow({ 262 270 agent: agent || undefined, 263 271 targetDid: identity?.did, 264 272 followRecords: followRecords, 265 273 queryClient: queryClient, 266 - }) 267 - } 268 - } 274 + }); 275 + }} 269 276 className="rounded-full h-10 bg-gray-300 dark:bg-gray-600 hover:bg-gray-400 dark:hover:bg-gray-500 transition-colors px-4 py-2 text-[14px]" 270 277 > 271 278 Unfollow ··· 281 288 ); 282 289 } 283 290 291 + export function Mutual({ targetdidorhandle }: { targetdidorhandle: string }) { 292 + const { agent } = useAuth(); 293 + const { data: identity } = useQueryIdentity(targetdidorhandle); 284 294 285 - export function Mutual({targetdidorhandle}:{targetdidorhandle: string}) { 286 - const {agent} = useAuth() 287 - const {data: identity} = useQueryIdentity(targetdidorhandle); 288 - 289 - const theyFollowYouRes = useGetOneToOneState(agent?.did ? { 290 - target: agent?.did, 291 - user: identity?.did ?? targetdidorhandle, 292 - collection: "app.bsky.graph.follow", 293 - path: ".subject" 294 - }:undefined); 295 + const theyFollowYouRes = useGetOneToOneState( 296 + agent?.did 297 + ? { 298 + target: agent?.did, 299 + user: identity?.did ?? targetdidorhandle, 300 + collection: "app.bsky.graph.follow", 301 + path: ".subject", 302 + } 303 + : undefined 304 + ); 295 305 296 306 const youFollowThemRes = useGetFollowState({ 297 307 target: identity?.did ?? targetdidorhandle, 298 308 user: agent?.did, 299 309 }); 300 310 301 - const theyFollowYou: boolean = (!!theyFollowYouRes?.length && theyFollowYouRes.length > 0) 302 - const youFollowThem: boolean = (!!youFollowThemRes?.length && youFollowThemRes.length > 0) 303 - 311 + const theyFollowYou: boolean = 312 + !!theyFollowYouRes?.length && theyFollowYouRes.length > 0; 313 + const youFollowThem: boolean = 314 + !!youFollowThemRes?.length && youFollowThemRes.length > 0; 315 + 304 316 return ( 305 317 <> 306 318 {/* if not self */} 307 319 {identity?.did !== agent?.did ? ( 308 320 <> 309 - {(theyFollowYou) ? ( 321 + {theyFollowYou ? ( 310 322 <> 311 323 {youFollowThem ? ( 312 - <div 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">mutuals</div> 313 - ) : ( 314 - <div 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">follows you</div> 315 - ) 316 - } 324 + <div 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"> 325 + mutuals 326 + </div> 327 + ) : ( 328 + <div 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"> 329 + follows you 330 + </div> 331 + )} 317 332 </> 318 333 ) : ( 319 334 <></> ··· 328 343 } 329 344 330 345 export function RichTextRenderer({ description }: { description: string }) { 331 - const [richDescription, setRichDescription] = useState<string | ReactNode[]>(description); 346 + const [richDescription, setRichDescription] = useState<string | ReactNode[]>( 347 + description 348 + ); 332 349 const { agent } = useAuth(); 333 350 const navigate = useNavigate(); 334 351 ··· 346 363 if (!mounted) return; 347 364 348 365 if (rt.facets) { 349 - setRichDescription(renderTextWithFacets({ text: rt.text, facets: rt.facets, navigate })); 366 + setRichDescription( 367 + renderTextWithFacets({ text: rt.text, facets: rt.facets, navigate }) 368 + ); 350 369 } else { 351 370 setRichDescription(rt.text); 352 371 } ··· 366 385 }, [description, agent, navigate]); 367 386 368 387 return <>{richDescription}</>; 369 - } 388 + }