| 1 | import { Tag } from "lucide-react"; |
| 2 | import Link from "next/link |
| 3 | |
| 4 | import { FromNowHoverCard } |
| 5 | import { Badge } |
| 6 | import { Skeleton } |
| 7 | import { createServerClient } |
| 8 | |
| 9 | import { releasePath } |
| 10 | |
| 11 | /** The repository sidebar's releases section: the latest release, and the way to every release and tag. */ |
| 12 | export async function LatestRelease |
| 13 | username, |
| 14 | repo, |
| 15 | }: { |
| 16 | username: string |
| 17 | repo: string |
| 18 | }) { |
| 19 | const client = |
| 20 | const { data, |
| 21 | "/api/repositories/{username}/{repo}/releases/latest", |
| 22 | { params: { path: |
| 23 | ); |
| 24 | // a 404 is "nothing published yet"; anything else without data is a failure, not an empty state |
| 25 | if (!data && |
| 26 | throw new Error( |
| 27 | `Failed to load the latest release of ${username}/${ |
| 28 | ); |
| 29 | } |
| 30 | const base = |
| 31 | |
| 32 | return ( |
| 33 | <div className="flex flex-col gap-3 |
| 34 | <h2 className="text-sm font-semibold |
| 35 | <Link href={ |
| 36 | Releases |
| 37 | </Link> |
| 38 | </h2> |
| 39 | |
| 40 | {data ? ( |
| 41 | <Link |
| 42 | href={releasePath(username |
| 43 | className="group flex items-start gap-2 text-sm" |
| 44 | > |
| 45 | <Tag className=" |
| 46 | <span className="flex min-w-0 flex-col gap-0.5 |
| 47 | <span className="flex items-center gap-2 |
| 48 | <span className="truncate font-medium group-hover:underline |
| 49 | {data.name ?? |
| 50 | </span> |
| 51 | <Badge variant=" |
| 52 | </span> |
| 53 | {data.publishedAt && |
| 54 | <FromNowHoverCard |
| 55 | date={data.publishedAt |
| 56 | className="text-xs text-muted-foreground" |
| 57 | /> |
| 58 | )} |
| 59 | </span> |
| 60 | </Link> |
| 61 | ) : ( |
| 62 | <p className="text-sm text-muted-foreground |
| 63 | )} |
| 64 | |
| 65 | <Link |
| 66 | href={`${base |
| 67 | className="text-xs text-muted-foreground hover:text-foreground hover:underline" |
| 68 | > |
| 69 | View all tags |
| 70 | </Link> |
| 71 | </div> |
| 72 | ); |
| 73 | } |
| 74 | |
| 75 | export function LatestReleaseSkeleton() |
| 76 | return ( |
| 77 | <div className="flex flex-col gap-3 |
| 78 | <Skeleton className=" |
| 79 | <div className="flex items-start gap-2 |
| 80 | <Skeleton className=" |
| 81 | <div className="flex flex-col gap-1 |
| 82 | <Skeleton className=" |
| 83 | <Skeleton className=" |
| 84 | </div> |
| 85 | </div> |
| 86 | </div> |
| 87 | ); |
| 88 | } |