Basic post viewer

This commit is contained in:
2024-12-17 22:12:19 +08:00
parent f730579eeb
commit fc4c884ade
11 changed files with 189 additions and 94 deletions

View File

@ -2,6 +2,12 @@
import { Image } from "astro:assets";
import CompanyLogo from "../assets/images/company-logo.png";
interface Props {
title?: string;
}
const { title } = Astro.props;
---
<!doctype html>
@ -10,7 +16,7 @@ import CompanyLogo from "../assets/images/company-logo.png";
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/png" href="/favicon.png" />
<title>Solsynth LLC</title>
<title>{title ? `${title} | Solsynth LLC` : "Solsynth LLC"}</title>
</head>
<body>
<div class="navbar backdrop-blur fixed top-0 left-0 right-0 z-10">
@ -20,7 +26,7 @@ import CompanyLogo from "../assets/images/company-logo.png";
<Image
src={CompanyLogo}
alt="company logo"
class="h-7 w-7 navbar-company-logo"
class="h-8 w-8 p-1 bg-white rounded-lg shadow-sm"
/>
<span>Solsynth</span>
</a>
@ -56,17 +62,4 @@ import CompanyLogo from "../assets/images/company-logo.png";
width: 100%;
height: 100%;
}
.navbar-company-logo {
-webkit-filter: drop-shadow(1px 1px 0 black) drop-shadow(-1px -1px 0 black);
filter: drop-shadow(1px 1px 0 black) drop-shadow(-1px -1px 0 black);
}
@media (prefers-color-scheme: dark) {
.navbar-company-logo {
-webkit-filter: drop-shadow(1px 1px 0 white)
drop-shadow(-1px -1px 0 white);
filter: drop-shadow(1px 1px 0 white) drop-shadow(-1px -1px 0 white);
}
}
</style>

View File

@ -59,7 +59,7 @@ import ProductSnPreviewImage from "../assets/images/product-solar-network.webp";
</a>
</div>
<a class="link flex items-center gap-1 mt-3">
<a class="link flex items-center gap-1 mt-3" href="/products/solar-network">
<span>Learn more about Solar Network</span>
<Icon name="material-symbols:arrow-right-alt" />
</a>

View File

@ -0,0 +1,108 @@
---
export const prerender = false
import sanitizeHtml from 'sanitize-html'
import { Icon } from 'astro-icon/components'
import { marked } from 'marked'
import Layout from '../../layouts/Layout.astro'
import { getAttachmentUrl, fetchAttachmentMeta } from '../../scripts/attachment'
const { slug } = Astro.params
const baseUrl = import.meta.env.PUBLIC_SOLAR_NETWORK_URL
const resp = await fetch(`${baseUrl}/cgi/co/posts/${slug}`)
if (resp.status !== 200) {
return new Response(null, { status: 404 })
}
const data = await resp.json()
const rawContent = await marked(data.body.content as string, {
breaks: data.type == 'story',
})
const content = sanitizeHtml(rawContent)
const attachments = await fetchAttachmentMeta(data.body.attachments)
---
<Layout title={data.body?.title ? data.body.title : `Post #${data.id}`}>
<div role="alert" class="alert shadow-lg px-12 m-0 rounded-none mb-5">
<Icon
name="material-symbols:ungroup"
class="stroke-info fill-info h-6 w-6 shrink-0"
/>
<div>
<h3 class="font-bold">Open in the Solian</h3>
<div class="text-xs">
The most modern, user-friendly, and official Solar Network app.
</div>
</div>
<div class="flex gap-2">
<a class="btn btn-sm" href="/products/solar-network">Get</a>
<a class="btn btn-sm" href={`https://sn.solsynth.dev/posts/${data.id}`}
>Open</a
>
</div>
</div>
<div class="container lg:max-w-[75ch] px-8 mx-auto">
<div class="flex gap-4 items-center mb-5">
<div class="avatar">
<div class="w-12 rounded-full">
<img src={getAttachmentUrl(data.publisher.avatar)} alt="avatar" />
</div>
</div>
<div class="userinfo flex flex-col">
<span class="flex gap-2 items-baseline">
<span class="text-md font-bold">{data.publisher.nick}</span>
<span class="text-xs font-mono">@{data.publisher.name}</span>
</span>
<span class="text-sm line-clamp-2 overflow-ellipsis"
>{data.publisher.description}</span
>
</div>
</div>
{
data.repost_id && (
<div role="alert" class="alert mb-5 py-2 mx-[-4px]">
<Icon
name="material-symbols:format-quote"
class="stroke-info fill-info h-6 w-6 shrink-0"
/>
<span>
This post is reposting post{' '}
<span class="font-mono">#{data.repost_id}</span>
</span>
<div>
<a class="btn btn-sm" href={`/posts/${data.repost_id}`}>
See reposted post
</a>
</div>
</div>
)
}
<article class="prose max-w-none max-md:prose-lg" set:html={content} />
{
attachments && (
<div class="attachment-list mt-5 gap-4 grid grid-cols-1 md:grid-cols-2">
{attachments.map((attachment) => (
<div class="attachment">
<a href={getAttachmentUrl(attachment.rid)} target="_blank">
<img
src={getAttachmentUrl(attachment.rid)}
alt={attachment.alt}
class="rounded-lg"
/>
</a>
</div>
))}
</div>
)
}
</div>
</Layout>

View File

@ -0,0 +1,7 @@
---
import Layout from "../../layouts/Layout.astro";
---
<Layout title="Solar Network">
</Layout>

23
src/scripts/attachment.ts Normal file
View File

@ -0,0 +1,23 @@
export function getAttachmentUrl(identifier: string): string {
if (identifier.startsWith('http')) {
return identifier
}
const baseUrl = import.meta.env.PUBLIC_SOLAR_NETWORK_URL
return `${baseUrl}/cgi/uc/attachments/${identifier}`
}
export async function fetchAttachmentMeta(
identifiers: string[]
): Promise<any[]> {
if (!identifiers) return []
const baseUrl = import.meta.env.PUBLIC_SOLAR_NETWORK_URL
const resp = await fetch(
`${baseUrl}/cgi/uc/attachments?take=${identifiers.length}&id=${identifiers.join(',')}`
)
if (resp.status !== 200) {
throw new Error(`Failed to fetch attachment meta: ${await resp.text()}`)
}
const out = await resp.json()
return out['data']
}