atmosphere explorer
0
fork

Configure Feed

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

handle record error inside the component

Juliet cbf7e783 e56b7c5f

+180 -169
+180 -169
src/views/record.tsx
··· 384 384 <Title> 385 385 {params.collection}/{params.rkey} - PDSls 386 386 </Title> 387 - <Show when={record()} keyed> 388 - <div class="flex w-full flex-col items-center"> 389 - <div class="mb-3 flex w-full justify-between px-2 text-sm sm:text-base"> 390 - <div class="flex items-center gap-4"> 391 - <RecordTab tab="record" label="Record" /> 392 - <RecordTab tab="schema" label="Schema" /> 393 - <RecordTab tab="backlinks" label="Backlinks" /> 394 - <RecordTab tab="info" label="Info" error /> 387 + <ErrorBoundary 388 + fallback={(err) => ( 389 + <div class="flex w-full flex-col items-center gap-1 px-2 py-4"> 390 + <span class="font-semibold text-red-500 dark:text-red-400">Error loading record</span> 391 + <div class="max-w-md text-sm wrap-break-word text-neutral-600 dark:text-neutral-400"> 392 + {err.message} 395 393 </div> 396 - <div class="flex gap-0.5"> 397 - <Show when={agent() && agent()?.sub === record()?.uri.split("/")[2]}> 398 - <Show when={hasUserScope("update")}> 399 - <RecordEditor create={false} record={record()?.value} refetch={refetch} /> 400 - </Show> 401 - <Show when={hasUserScope("delete")}> 402 - <Tooltip text="Delete"> 403 - <button 404 - class="flex items-center rounded-sm p-1.5 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 405 - onclick={() => setOpenDelete(true)} 406 - > 407 - <span class="iconify lucide--trash-2"></span> 408 - </button> 409 - </Tooltip> 410 - <Modal open={openDelete()} onClose={() => setOpenDelete(false)}> 411 - <div class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-70 left-[50%] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 412 - <h2 class="mb-2 font-semibold">Delete this record?</h2> 413 - <div class="flex justify-end gap-2"> 414 - <Button onClick={() => setOpenDelete(false)}>Cancel</Button> 415 - <Button 416 - onClick={deleteRecord} 417 - class="dark:shadow-dark-700 rounded-lg bg-red-500 px-2 py-1.5 text-xs text-white shadow-xs select-none hover:bg-red-400 active:bg-red-400" 418 - > 419 - Delete 420 - </Button> 394 + </div> 395 + )} 396 + > 397 + <Show when={record()} keyed> 398 + <div class="flex w-full flex-col items-center"> 399 + <div class="mb-3 flex w-full justify-between px-2 text-sm sm:text-base"> 400 + <div class="flex items-center gap-4"> 401 + <RecordTab tab="record" label="Record" /> 402 + <RecordTab tab="schema" label="Schema" /> 403 + <RecordTab tab="backlinks" label="Backlinks" /> 404 + <RecordTab tab="info" label="Info" error /> 405 + </div> 406 + <div class="flex gap-0.5"> 407 + <Show when={agent() && agent()?.sub === record()?.uri.split("/")[2]}> 408 + <Show when={hasUserScope("update")}> 409 + <RecordEditor create={false} record={record()?.value} refetch={refetch} /> 410 + </Show> 411 + <Show when={hasUserScope("delete")}> 412 + <Tooltip text="Delete"> 413 + <button 414 + class="flex items-center rounded-sm p-1.5 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600" 415 + onclick={() => setOpenDelete(true)} 416 + > 417 + <span class="iconify lucide--trash-2"></span> 418 + </button> 419 + </Tooltip> 420 + <Modal open={openDelete()} onClose={() => setOpenDelete(false)}> 421 + <div class="dark:bg-dark-300 dark:shadow-dark-700 absolute top-70 left-[50%] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-4 shadow-md transition-opacity duration-200 dark:border-neutral-700 starting:opacity-0"> 422 + <h2 class="mb-2 font-semibold">Delete this record?</h2> 423 + <div class="flex justify-end gap-2"> 424 + <Button onClick={() => setOpenDelete(false)}>Cancel</Button> 425 + <Button 426 + onClick={deleteRecord} 427 + class="dark:shadow-dark-700 rounded-lg bg-red-500 px-2 py-1.5 text-xs text-white shadow-xs select-none hover:bg-red-400 active:bg-red-400" 428 + > 429 + Delete 430 + </Button> 431 + </div> 421 432 </div> 422 - </div> 423 - </Modal> 433 + </Modal> 434 + </Show> 424 435 </Show> 425 - </Show> 426 - <MenuProvider> 427 - <DropdownMenu icon="lucide--ellipsis" buttonClass="rounded-sm p-1.5"> 428 - <CopyMenu 429 - content={JSON.stringify(record()?.value, null, 2)} 430 - label="Copy record" 431 - icon="lucide--copy" 432 - /> 433 - <CopyMenu 434 - content={`at://${params.repo}/${params.collection}/${params.rkey}`} 435 - label="Copy AT URI" 436 - icon="lucide--copy" 437 - /> 438 - <Show when={record()?.cid}> 439 - {(cid) => <CopyMenu content={cid()} label="Copy CID" icon="lucide--copy" />} 440 - </Show> 441 - <MenuSeparator /> 442 - <Show when={externalLink()}> 443 - {(externalLink) => ( 444 - <NavMenu 445 - href={externalLink()?.link} 446 - icon={`${externalLink().icon ?? "lucide--app-window"}`} 447 - label={`Open on ${externalLink().label}`} 448 - newTab 449 - /> 450 - )} 451 - </Show> 452 - <NavMenu 453 - href={`https://${pds()}/xrpc/com.atproto.repo.getRecord?repo=${params.repo}&collection=${params.collection}&rkey=${params.rkey}`} 454 - icon="lucide--external-link" 455 - label="Record on PDS" 456 - newTab 457 - /> 458 - </DropdownMenu> 459 - </MenuProvider> 436 + <MenuProvider> 437 + <DropdownMenu icon="lucide--ellipsis" buttonClass="rounded-sm p-1.5"> 438 + <CopyMenu 439 + content={JSON.stringify(record()?.value, null, 2)} 440 + label="Copy record" 441 + icon="lucide--copy" 442 + /> 443 + <CopyMenu 444 + content={`at://${params.repo}/${params.collection}/${params.rkey}`} 445 + label="Copy AT URI" 446 + icon="lucide--copy" 447 + /> 448 + <Show when={record()?.cid}> 449 + {(cid) => <CopyMenu content={cid()} label="Copy CID" icon="lucide--copy" />} 450 + </Show> 451 + <MenuSeparator /> 452 + <Show when={externalLink()}> 453 + {(externalLink) => ( 454 + <NavMenu 455 + href={externalLink()?.link} 456 + icon={`${externalLink().icon ?? "lucide--app-window"}`} 457 + label={`Open on ${externalLink().label}`} 458 + newTab 459 + /> 460 + )} 461 + </Show> 462 + <NavMenu 463 + href={`https://${pds()}/xrpc/com.atproto.repo.getRecord?repo=${params.repo}&collection=${params.collection}&rkey=${params.rkey}`} 464 + icon="lucide--external-link" 465 + label="Record on PDS" 466 + newTab 467 + /> 468 + </DropdownMenu> 469 + </MenuProvider> 470 + </div> 460 471 </div> 461 - </div> 462 - <Show when={!location.hash || location.hash === "#record"}> 463 - <div class="w-max max-w-screen min-w-full px-4 font-mono text-xs wrap-anywhere whitespace-pre-wrap sm:px-2 sm:text-sm md:max-w-3xl"> 464 - <JSONValue data={record()?.value as any} repo={record()!.uri.split("/")[2]} /> 465 - </div> 466 - </Show> 467 - <Show when={location.hash === "#schema" || location.hash.startsWith("#schema:")}> 468 - <Show when={lexiconNotFound() === true}> 469 - <span class="w-full px-2 text-sm">Lexicon schema could not be resolved.</span> 472 + <Show when={!location.hash || location.hash === "#record"}> 473 + <div class="w-max max-w-screen min-w-full px-4 font-mono text-xs wrap-anywhere whitespace-pre-wrap sm:px-2 sm:text-sm md:max-w-3xl"> 474 + <JSONValue data={record()?.value as any} repo={record()!.uri.split("/")[2]} /> 475 + </div> 470 476 </Show> 471 - <Show when={lexiconNotFound() === undefined}> 472 - <span class="w-full px-2 text-sm">Resolving lexicon schema...</span> 477 + <Show when={location.hash === "#schema" || location.hash.startsWith("#schema:")}> 478 + <Show when={lexiconNotFound() === true}> 479 + <span class="w-full px-2 text-sm">Lexicon schema could not be resolved.</span> 480 + </Show> 481 + <Show when={lexiconNotFound() === undefined}> 482 + <span class="w-full px-2 text-sm">Resolving lexicon schema...</span> 483 + </Show> 484 + <Show when={schema() || params.collection === "com.atproto.lexicon.schema"}> 485 + <ErrorBoundary fallback={(err) => <div>Error: {err.message}</div>}> 486 + <LexiconSchemaView schema={schema()?.rawSchema ?? (record()?.value as any)} /> 487 + </ErrorBoundary> 488 + </Show> 473 489 </Show> 474 - <Show when={schema() || params.collection === "com.atproto.lexicon.schema"}> 475 - <ErrorBoundary fallback={(err) => <div>Error: {err.message}</div>}> 476 - <LexiconSchemaView schema={schema()?.rawSchema ?? (record()?.value as any)} /> 490 + <Show when={location.hash === "#backlinks"}> 491 + <ErrorBoundary 492 + fallback={(err) => <div class="wrap-break-word">Error: {err.message}</div>} 493 + > 494 + <Suspense 495 + fallback={ 496 + <div class="iconify lucide--loader-circle animate-spin self-center text-xl" /> 497 + } 498 + > 499 + <div class="w-full px-2"> 500 + <Backlinks target={`at://${did}/${params.collection}/${params.rkey}`} /> 501 + </div> 502 + </Suspense> 477 503 </ErrorBoundary> 478 504 </Show> 479 - </Show> 480 - <Show when={location.hash === "#backlinks"}> 481 - <ErrorBoundary 482 - fallback={(err) => <div class="wrap-break-word">Error: {err.message}</div>} 483 - > 484 - <Suspense 485 - fallback={ 486 - <div class="iconify lucide--loader-circle animate-spin self-center text-xl" /> 487 - } 488 - > 489 - <div class="w-full px-2"> 490 - <Backlinks target={`at://${did}/${params.collection}/${params.rkey}`} /> 505 + <Show when={location.hash === "#info"}> 506 + <div class="flex w-full flex-col gap-2 px-2 text-sm"> 507 + <div> 508 + <p class="font-semibold">AT URI</p> 509 + <div class="truncate text-xs">{record()?.uri}</div> 491 510 </div> 492 - </Suspense> 493 - </ErrorBoundary> 494 - </Show> 495 - <Show when={location.hash === "#info"}> 496 - <div class="flex w-full flex-col gap-2 px-2 text-sm"> 497 - <div> 498 - <p class="font-semibold">AT URI</p> 499 - <div class="truncate text-xs">{record()?.uri}</div> 500 - </div> 501 - <Show when={record()?.cid}> 502 - <div> 503 - <p class="font-semibold">CID</p> 504 - <div class="truncate text-left text-xs" dir="rtl"> 505 - {record()?.cid} 511 + <Show when={record()?.cid}> 512 + <div> 513 + <p class="font-semibold">CID</p> 514 + <div class="truncate text-left text-xs" dir="rtl"> 515 + {record()?.cid} 516 + </div> 506 517 </div> 507 - </div> 508 - </Show> 509 - <div> 510 - <div class="flex items-center gap-1"> 511 - <p class="font-semibold">Record verification</p> 512 - <span 513 - classList={{ 514 - "iconify lucide--check text-green-500 dark:text-green-400": 515 - validRecord() === true, 516 - "iconify lucide--x text-red-500 dark:text-red-400": validRecord() === false, 517 - "iconify lucide--loader-circle animate-spin": validRecord() === undefined, 518 - }} 519 - ></span> 520 - </div> 521 - <Show when={validRecord() === false}> 522 - <div class="text-xs wrap-break-word">{verifyError()}</div> 523 518 </Show> 524 - </div> 525 - <div> 526 - <div class="flex items-center gap-1"> 527 - <p class="font-semibold">Schema validation</p> 528 - <span 529 - classList={{ 530 - "iconify lucide--check text-green-500 dark:text-green-400": 531 - validSchema() === true, 532 - "iconify lucide--x text-red-500 dark:text-red-400": validSchema() === false, 533 - "iconify lucide--loader-circle animate-spin": 534 - validSchema() === undefined && remoteValidation(), 535 - }} 536 - ></span> 519 + <div> 520 + <div class="flex items-center gap-1"> 521 + <p class="font-semibold">Record verification</p> 522 + <span 523 + classList={{ 524 + "iconify lucide--check text-green-500 dark:text-green-400": 525 + validRecord() === true, 526 + "iconify lucide--x text-red-500 dark:text-red-400": validRecord() === false, 527 + "iconify lucide--loader-circle animate-spin": validRecord() === undefined, 528 + }} 529 + ></span> 530 + </div> 531 + <Show when={validRecord() === false}> 532 + <div class="text-xs wrap-break-word">{verifyError()}</div> 533 + </Show> 537 534 </div> 538 - <Show when={validSchema() === false}> 539 - <div class="text-xs wrap-break-word">{validationError()}</div> 540 - </Show> 541 - <Show 542 - when={ 543 - !remoteValidation() && 544 - validSchema() === undefined && 545 - params.collection && 546 - !(params.collection in lexicons) 547 - } 548 - > 549 - <Button onClick={() => validateRemoteSchema(record()!.value)}> 550 - Validate via resolution 551 - </Button> 552 - </Show> 553 - </div> 554 - <Show when={lexiconUri()}> 555 535 <div> 556 - <p class="font-semibold">Lexicon schema</p> 557 - <div class="truncate text-xs"> 558 - <A 559 - href={`/${lexiconUri()}`} 560 - class="text-blue-400 hover:underline active:underline" 561 - > 562 - {lexiconUri()} 563 - </A> 536 + <div class="flex items-center gap-1"> 537 + <p class="font-semibold">Schema validation</p> 538 + <span 539 + classList={{ 540 + "iconify lucide--check text-green-500 dark:text-green-400": 541 + validSchema() === true, 542 + "iconify lucide--x text-red-500 dark:text-red-400": validSchema() === false, 543 + "iconify lucide--loader-circle animate-spin": 544 + validSchema() === undefined && remoteValidation(), 545 + }} 546 + ></span> 564 547 </div> 548 + <Show when={validSchema() === false}> 549 + <div class="text-xs wrap-break-word">{validationError()}</div> 550 + </Show> 551 + <Show 552 + when={ 553 + !remoteValidation() && 554 + validSchema() === undefined && 555 + params.collection && 556 + !(params.collection in lexicons) 557 + } 558 + > 559 + <Button onClick={() => validateRemoteSchema(record()!.value)}> 560 + Validate via resolution 561 + </Button> 562 + </Show> 565 563 </div> 566 - </Show> 567 - </div> 568 - </Show> 569 - </div> 570 - </Show> 564 + <Show when={lexiconUri()}> 565 + <div> 566 + <p class="font-semibold">Lexicon schema</p> 567 + <div class="truncate text-xs"> 568 + <A 569 + href={`/${lexiconUri()}`} 570 + class="text-blue-400 hover:underline active:underline" 571 + > 572 + {lexiconUri()} 573 + </A> 574 + </div> 575 + </div> 576 + </Show> 577 + </div> 578 + </Show> 579 + </div> 580 + </Show> 581 + </ErrorBoundary> 571 582 </> 572 583 ); 573 584 };