feat: add bookmarks page
6d5c4312
3 file(s) · +83 −0
| 1 | + | --- |
|
| 2 | + | type Link = { |
|
| 3 | + | id: number; |
|
| 4 | + | short_id: string; |
|
| 5 | + | title: string; |
|
| 6 | + | url: string; |
|
| 7 | + | category_id: number; |
|
| 8 | + | created_at: number; |
|
| 9 | + | }; |
|
| 10 | + | ||
| 11 | + | const { link } = Astro.props as { link: Link }; |
|
| 12 | + | const domain = new URL(link.url).hostname.replace(/^www\./, ""); |
|
| 13 | + | --- |
|
| 14 | + | ||
| 15 | + | <article class="flex flex-col py-3 border-b border-[#333]"> |
|
| 16 | + | <a |
|
| 17 | + | href={link.url} |
|
| 18 | + | target="_blank" |
|
| 19 | + | rel="noopener noreferrer" |
|
| 20 | + | class="text-base hover:opacity-70 transition-opacity" |
|
| 21 | + | >{link.title}</a> |
|
| 22 | + | <span class="text-xs opacity-50 mt-0.5">{domain}</span> |
|
| 23 | + | </article> |
| 40 | 40 | path: "/library", |
|
| 41 | 41 | }, |
|
| 42 | 42 | { |
|
| 43 | + | title: "Bookmarks", |
|
| 44 | + | path: "/bookmarks", |
|
| 45 | + | }, |
|
| 46 | + | { |
|
| 43 | 47 | title: "Git", |
|
| 44 | 48 | path: "/git", |
|
| 45 | 49 | }, |
| 1 | + | --- |
|
| 2 | + | export const prerender = false; |
|
| 3 | + | ||
| 4 | + | import PageLayout from "@/layouts/Base.astro"; |
|
| 5 | + | import LinkCard from "@/components/page/LinkCard.astro"; |
|
| 6 | + | ||
| 7 | + | type Link = { |
|
| 8 | + | id: number; |
|
| 9 | + | short_id: string; |
|
| 10 | + | title: string; |
|
| 11 | + | url: string; |
|
| 12 | + | category_id: number; |
|
| 13 | + | created_at: number; |
|
| 14 | + | }; |
|
| 15 | + | ||
| 16 | + | const meta = { |
|
| 17 | + | title: "Bookmarks", |
|
| 18 | + | description: "Links I've saved and want to read", |
|
| 19 | + | }; |
|
| 20 | + | ||
| 21 | + | let categories: [string, Link[]][] = []; |
|
| 22 | + | let error: string | null = null; |
|
| 23 | + | try { |
|
| 24 | + | const res = await fetch("https://bookmarks.stevedylan.dev/api/links"); |
|
| 25 | + | if (res.ok) { |
|
| 26 | + | const data: Record<string, Link[]> = await res.json(); |
|
| 27 | + | categories = Object.entries(data).filter(([, links]) => links.length > 0); |
|
| 28 | + | } else { |
|
| 29 | + | error = `API returned ${res.status}`; |
|
| 30 | + | } |
|
| 31 | + | } catch (e) { |
|
| 32 | + | error = e instanceof Error ? e.message : "Failed to reach bookmarks API"; |
|
| 33 | + | } |
|
| 34 | + | --- |
|
| 35 | + | ||
| 36 | + | <PageLayout meta={meta}> |
|
| 37 | + | <div class="flex min-h-screen flex-col items-start justify-start gap-6"> |
|
| 38 | + | <h1 class="title">Bookmarks</h1> |
|
| 39 | + | {error ? ( |
|
| 40 | + | <p class="text-red-400 text-sm">Could not load bookmarks: {error}</p> |
|
| 41 | + | ) : categories.length === 0 ? ( |
|
| 42 | + | <p class="text-gray-400 text-sm">no bookmarks yet</p> |
|
| 43 | + | ) : ( |
|
| 44 | + | <div class="flex flex-col w-full gap-8"> |
|
| 45 | + | {categories.map(([name, links]) => ( |
|
| 46 | + | <div class="flex flex-col w-full"> |
|
| 47 | + | <h2 class="text-sm font-semibold uppercase tracking-widest opacity-50 mb-2">{name}</h2> |
|
| 48 | + | {links.map((link) => ( |
|
| 49 | + | <LinkCard link={link} /> |
|
| 50 | + | ))} |
|
| 51 | + | </div> |
|
| 52 | + | ))} |
|
| 53 | + | </div> |
|
| 54 | + | )} |
|
| 55 | + | </div> |
|
| 56 | + | </PageLayout> |