Testing implementation for private data in ATProto with ATPKeyserver and ATCute tools

ui to see feeds from different users

+50 -19
+5
packages/client/app/components/PostFeed.tsx
··· 9 9 export default function PostFeed({ feed }: { feed: FeedItem[] }) { 10 10 return ( 11 11 <ul className="divide-accent-content"> 12 + {feed.length === 0 ? ( 13 + <li className="p-4"> 14 + <p className="text-center">No posts here</p> 15 + </li> 16 + ) : null} 12 17 {feed.map((post) => ( 13 18 <li 14 19 key={post.uri}
+15 -2
packages/client/app/lib/post.server.ts
··· 3 3 parse, 4 4 parseCanonicalResourceUri, 5 5 type Did, 6 + type Handle, 6 7 type ResourceUri 7 8 } from '@atcute/lexicons' 8 9 import { createKeyClient, getServiceAgent, getSessionAgent } from './xrpcClient' ··· 129 130 visibility: (string & {}) | 'followers' | 'mentioned' | 'public' 130 131 } 131 132 132 - export async function getFeed(request: Request) { 133 + export async function getFeed(request: Request, handle?: Handle) { 133 134 const serviceDid = 134 135 `did:web:${encodeURIComponent(new URL(env.API_URL).host)}` as const 135 136 const session = await getOAuthSession(request) 136 137 const serverClient = await getServiceAgent(session, serviceDid) 137 138 const keyClient = createKeyClient(session) 139 + let did = session.did as Did 140 + if (handle) { 141 + const foundDid = await handleToDid(handle) 142 + if (foundDid) { 143 + did = foundDid 144 + } 145 + } 138 146 139 147 const { feed } = await ok( 140 148 serverClient.get('app.wafrn.content.getFeed', { 141 - params: { did: session.did } 149 + params: { did } 142 150 }) 143 151 ) 144 152 ··· 192 200 const didDoc = await idResolver.did.resolveAtprotoData(did) 193 201 return didDoc.handle 194 202 } 203 + 204 + async function handleToDid(handle: Handle) { 205 + const did = await idResolver.handle.resolve(handle) 206 + return did as Did | undefined 207 + }
+10 -17
packages/client/app/routes/_index.tsx
··· 1 1 import { useRootData } from '@www/lib/useRootData' 2 2 import { Link } from 'react-router' 3 - import type { Route } from './+types/_index' 4 - import { getFeed } from '@www/lib/post.server' 5 - import { useLoaderData } from 'react-router' 6 - import PostFeed from '@www/components/PostFeed' 7 3 8 4 export function meta() { 9 5 return [ 10 6 { title: 'New React Router App' }, 11 7 { name: 'description', content: 'Welcome to React Router!' } 12 8 ] 13 - } 14 - 15 - export async function loader({ request }: Route.LoaderArgs) { 16 - try { 17 - const feed = await getFeed(request) 18 - return { feed } 19 - } catch (err) { 20 - console.error(err) 21 - return { feed: [] } 22 - } 23 9 } 24 10 25 11 export default function Home() { 26 12 const { user } = useRootData() 27 - const { feed } = useLoaderData<typeof loader>() 28 - console.log('client feed: ', feed) 29 13 30 14 return ( 31 15 <div className="p-4 max-w-3xl mx-auto"> ··· 40 24 </Link> 41 25 )} 42 26 </p> 43 - <PostFeed feed={feed} /> 27 + <p> 28 + <Link className="btn btn-link" to="/user/blt.pds.djara.dev"> 29 + See posts from @blt.pds.djara.dev 30 + </Link> 31 + </p> 32 + <p> 33 + <Link className="btn btn-link" to="/user/user2.pds.djara.dev"> 34 + See posts from @user2.pds.djara.dev 35 + </Link> 36 + </p> 44 37 </div> 45 38 ) 46 39 }
+20
packages/client/app/routes/user.$handle.tsx
··· 1 + import type { Route } from './+types/user.$handle' 2 + import { getFeed } from '@www/lib/post.server' 3 + import type { Handle } from '@atcute/lexicons' 4 + import { useLoaderData } from 'react-router' 5 + import PostFeed from '@www/components/PostFeed' 6 + 7 + export async function loader({ request, params }: Route.LoaderArgs) { 8 + const feed = await getFeed(request, params.handle as Handle) 9 + return { feed } 10 + } 11 + 12 + export default function UserPage() { 13 + const { feed } = useLoaderData<typeof loader>() 14 + 15 + return ( 16 + <div> 17 + <PostFeed feed={feed} /> 18 + </div> 19 + ) 20 + }