website/www/src/pages/index.astro

89 lines
4.3 KiB
Text

---
import { getCollection } from 'astro:content';
import Layout from '../layouts/Layout.astro';
import { getApprovedEntries, type GuestbookEntry } from '../lib/db';
import { formatDate, formatListItem, escapeHtml } from '../lib/format';
import { organizePostsByCategory, getSlug, enrichPostsWithDates } from '../lib/md';
import { getTxtFiles } from '../lib/txt';
import { DEFAULT_CATEGORY, SECTIONS, SUBDOMAINS } from '../lib/consts';
const rawPosts = await getCollection('md');
const posts = enrichPostsWithDates(rawPosts);
const { grouped, categories: sortedCategories } = organizePostsByCategory(posts);
const bookmarksCollection = await getCollection('bookmarks');
const bookmarks = bookmarksCollection
.sort((a, b) => b.data.date.getTime() - a.data.date.getTime());
const txtFiles = getTxtFiles();
const txtMaxNameLen = Math.max(...txtFiles.map(f => f.name.replace(/\.txt$/, '').length));
const visibleLabels = [...sortedCategories.filter(c => c !== DEFAULT_CATEGORY), ...Object.values(SECTIONS)];
const labelWidth = Math.max(...visibleLabels.map(l => l.length));
const labelPrefix = (label: string, href: string) =>
`${' '.repeat(labelWidth - label.length)}<a class="section-label" href="${href}">${label}</a> `;
const blankPrefix = ' '.repeat(labelWidth + 2);
let guestbookEntries: GuestbookEntry[] = [];
try {
guestbookEntries = await getApprovedEntries();
} catch {
// DB not available during dev without env vars
}
const urls = [
...posts.map(post => ({ url: `/${getSlug(post.id)}`, date: post.dates.created.getTime() })),
...txtFiles.map(f => ({ url: `/${f.name}`, date: f.date.getTime() })),
].sort((a, b) => b.date - a.date).map(e => e.url).concat(SUBDOMAINS);
---
<Layout title="lewis m.w." isHome urls={urls}>
{sortedCategories.map(category => {
const categoryPosts = grouped[category];
const isDefault = category === DEFAULT_CATEGORY;
return (
<section data-section={category}>
<pre set:html={categoryPosts.map((post, i) => formatListItem(post.dates.created, `/${getSlug(post.id)}`, post.data.title, { pinned: post.data.pinned, prefix: (!isDefault && i === 0) ? labelPrefix(category, `?just=${category}`) : blankPrefix })).join('\n')} />
</section>
);
})}
<section data-section={SECTIONS.plaintext}>
<pre set:html={txtFiles.map((f, i) => {
const name = f.name.replace(/\.txt$/, '');
const pad = ' '.repeat(txtMaxNameLen - name.length);
const suffix = f.description ? `${pad} <span class="muted">(${f.description})</span>` : undefined;
return formatListItem(f.date, `/${f.name}`, name, { pinned: f.pinned, suffix, prefix: i === 0 ? labelPrefix(SECTIONS.plaintext, `?just=${SECTIONS.plaintext}`) : blankPrefix });
}).join('\n')} />
</section>
<section data-section={SECTIONS.bookmarks}>
<pre set:html={bookmarks.map((b, i) => formatListItem(b.data.date, b.data.url, b.data.title, { prefix: i === 0 ? labelPrefix(SECTIONS.bookmarks, `?just=${SECTIONS.bookmarks}`) : blankPrefix })).join('\n')} />
</section>
<section data-section={SECTIONS.guestbook}>
<div class="guestbook-entries" style={`--meta-width: ${labelWidth + 12}ch`} set:html={guestbookEntries.map((e, i) => {
const prefix = i === 0 ? labelPrefix(SECTIONS.guestbook, `?just=${SECTIONS.guestbook}`) : blankPrefix;
const safeName = escapeHtml(e.name);
const safeMessage = escapeHtml(e.message.replace(/\n/g, ' '));
const nameHtml = e.url ? `<a href="${escapeHtml(e.url)}"><b>${safeName}</b></a>` : `<b>${safeName}</b>`;
return `<span class="guestbook-entry"><span class="list-meta">${prefix}<span class="muted">${formatDate(e.createdAt)}</span> </span><span>${nameHtml} ${safeMessage}</span></span>`;
}).join('')} /></div>
<form id="guestbook-form" class="guestbook-form" style={`margin-left: ${labelWidth + 12}ch`}>
<label class="sr-only" for="gb-name">name</label>
<input id="gb-name" type="text" name="name" placeholder="name" required maxlength="100" /><br />
<label class="sr-only" for="gb-message">message</label>
<input id="gb-message" type="text" name="message" placeholder="message" required maxlength="500" /><br />
<label class="sr-only" for="gb-url">url</label>
<input id="gb-url" type="url" name="url" placeholder="url (optional)" maxlength="200" /><br />
<button type="submit">sign</button>
<span id="guestbook-status"></span>
</form>
</section>
<script>
import { initGuestbookForm } from '../scripts/guestbook-sign';
initGuestbookForm();
</script>
</Layout>