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

refactor error handling

+23 -31
+23 -31
packages/client/app/routes/settings.delegation.tsx
··· 1 - import { Form, redirect, data } from 'react-router' 1 + import { Form, data } from 'react-router' 2 2 import type { Route } from './+types/settings.delegation' 3 3 import { getOAuthSession } from '@www/lib/oauth.server' 4 4 import { env } from '@www/lib/env.server' 5 5 import { createKeyClient } from '@www/lib/xrpcClient' 6 + import asyncWrap from '@www/lib/asyncWrap' 6 7 7 - interface DelegateInfo { 8 - delegateDid: string 9 - permissions: string[] 10 - grantedAt: string 11 - expiresAt?: string 8 + const defaultData = { 9 + delegates: [], 10 + groupId: '', 11 + apiServiceDid: '', 12 + userDid: '' 12 13 } 13 14 14 15 export async function loader({ request }: Route.LoaderArgs) { ··· 16 17 const groupId = `${session.did}#followers` 17 18 const keyClient = createKeyClient(session) 18 19 19 - try { 20 + const [data, error] = await asyncWrap(async () => { 20 21 const result = await keyClient.listDelegates({ group_id: groupId }) 21 22 return { 22 23 delegates: result.delegates, ··· 24 25 apiServiceDid: env.API_SERVICE_DID, 25 26 userDid: session.did 26 27 } 27 - } catch (error) { 28 - // If group doesn't exist yet or error listing, return empty 29 - return { 30 - delegates: [], 31 - groupId, 32 - apiServiceDid: env.API_SERVICE_DID, 33 - userDid: session.did 34 - } 35 - } 28 + }) 29 + return { data, error } 36 30 } 37 31 38 32 export async function action({ request }: Route.ActionArgs) { ··· 49 43 delegate_did: env.API_SERVICE_DID, 50 44 permissions: ['add_member', 'remove_member'] 51 45 }) 52 - return data( 53 - { success: true, message: 'Server authorized successfully!' }, 54 - { status: 200 } 55 - ) 46 + return data({ success: true, message: 'Server authorized successfully!' }) 56 47 } else if (action === 'revoke') { 57 48 await keyClient.revokeDelegate({ 58 49 group_id: groupId, 59 50 delegate_did: env.API_SERVICE_DID 60 51 }) 61 - return data( 62 - { success: true, message: 'Server authorization revoked!' }, 63 - { status: 200 } 64 - ) 52 + return data({ success: true, message: 'Server authorization revoked!' }) 65 53 } 66 54 } catch (error) { 67 55 return data( ··· 72 60 { status: 500 } 73 61 ) 74 62 } 75 - 76 - return redirect('/settings/delegation') 77 63 } 78 64 79 65 export default function DelegationSettings({ 80 66 loaderData, 81 67 actionData 82 68 }: Route.ComponentProps) { 83 - const { delegates, groupId, apiServiceDid, userDid } = loaderData 69 + const error = loaderData.error 70 + const { delegates, groupId, apiServiceDid, userDid } = 71 + loaderData.data ?? defaultData 84 72 const isAuthorized = delegates.some((d) => d.delegateDid === apiServiceDid) 85 73 86 74 return ( 87 - <div className="p-4 max-w-3xl mx-auto"> 88 - <h1 className="text-2xl font-bold mb-4">Delegation Settings</h1> 75 + <div className="px-4 py-6 max-w-3xl mx-auto"> 76 + <h1 className="text-2xl font-bold mb-3">Delegation Settings</h1> 89 77 90 78 <div className="card bg-base-200 shadow-xl mb-4"> 91 79 <div className="card-body"> ··· 116 104 </div> 117 105 </div> 118 106 119 - {actionData?.message && ( 107 + {actionData?.message ? ( 120 108 <div 121 109 className={`alert ${actionData.success ? 'alert-success' : 'alert-error'} mb-4`} 122 110 > 123 111 {actionData.message} 124 112 </div> 125 - )} 113 + ) : null} 114 + 115 + {error ? ( 116 + <div className="alert alert-error mb-4">{error.message}</div> 117 + ) : null} 126 118 127 119 <Form method="POST" className="card-actions"> 128 120 {isAuthorized ? (