Compare commits
2 Commits
eb067b6a39
...
0e4c438869
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e4c438869 | |||
| 8f6de2cced |
@@ -13,6 +13,13 @@ interface Props {
|
||||
|
||||
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
|
||||
|
||||
const blogTagMatch = Astro.url.pathname.match(/blog\/tag\/(\w+)/);
|
||||
|
||||
const rssUrl =
|
||||
blogTagMatch
|
||||
? new URL(`blog/tag/${blogTagMatch[1]}.rss.xml`, Astro.site)
|
||||
: new URL(`rss.xml`, Astro.site);
|
||||
|
||||
const { title, description, image } = Astro.props;
|
||||
---
|
||||
|
||||
@@ -25,7 +32,7 @@ const { title, description, image } = Astro.props;
|
||||
rel="alternate"
|
||||
type="application/rss+xml"
|
||||
title={SITE_TITLE}
|
||||
href={new URL('rss.xml', Astro.site)}
|
||||
href={rssUrl}
|
||||
/>
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
// You can import this data from anywhere in your site by using the `import` keyword.
|
||||
|
||||
export const SITE_TITLE = "Blazestar.net";
|
||||
export const SITE_DESCRIPTION = "Welcome to Blazestar.net";
|
||||
export const SITE_DESCRIPTION = "Periodic musings about systems";
|
||||
|
||||
@@ -15,6 +15,15 @@ The web has turned into a group of five websites, each consisting of screenshots
|
||||
|
||||
I'm old enough to have grown up in an age where personal websites were common, though many were hosted through sites like the long-gone [GeoCities](https://en.wikipedia.org/wiki/GeoCities) and [AngelFire](https://en.wikipedia.org/wiki/Angelfire). The web was a lot of little sites where people shared their passion or just a little bit about themselves. Search engines used to be one of the major tools for finding these sites, along with directories and [web rings](https://en.wikipedia.org/wiki/Webring). I spent many a teenage year browsing around sites looking for more information on Magic: the Gathering, Dungeons & Dragons, or learning how to build my own sites and wrangle with that new-fangled CSS thing.
|
||||
|
||||
## What is the Indie Web
|
||||
|
||||
The Indie Web seems to have come out of Bruce Sterling's IndieWeb Camp way back in 2013. There he outlined a few major principles, which are [largely unchanged today](https://indieweb.org/principles).
|
||||
|
||||
I think it can largely be summed up by two major themes:
|
||||
|
||||
- Build things for people. You are the first customer so design for yourself and what you love. Don't agonize about making things interoperable for machines.
|
||||
- Build things that last. You should have control over the data and site. It should only loosely depend on APIs and platform
|
||||
|
||||
So how do we go about joining the Indie Web?
|
||||
|
||||
## Step 1: Have a website
|
||||
|
||||
@@ -1,31 +1,20 @@
|
||||
---
|
||||
import NotchedBox from '../components/NotchedBox.astro';
|
||||
import RootLayout from '../layouts/RootLayout.astro';
|
||||
import { getCollection, type CollectionEntry } from 'astro:content';
|
||||
import FormattedDate from '../components/FormattedDate.astro';
|
||||
import { Image } from 'astro:assets';
|
||||
import { getPublishedPosts, getAllTags } from '../lib/blog';
|
||||
|
||||
interface Params {
|
||||
export interface Params {
|
||||
selectedTag?: string | undefined;
|
||||
}
|
||||
|
||||
const { selectedTag } = Astro.props;
|
||||
|
||||
function publishedOnly(p: CollectionEntry<'blog'>): p is (CollectionEntry<'blog'> & { data: { pubDate: Date }}) {
|
||||
return p.data.pubDate !== undefined;
|
||||
}
|
||||
const tags = await getAllTags();
|
||||
|
||||
const allPosts = (await getCollection('blog', publishedOnly))
|
||||
.sort(
|
||||
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
||||
);
|
||||
const posts = await getPublishedPosts( selectedTag );
|
||||
|
||||
const tags = allPosts.flatMap(p => p.data.tags);
|
||||
|
||||
const posts =
|
||||
selectedTag
|
||||
? allPosts.filter(p => p.data.tags.includes(selectedTag))
|
||||
: allPosts;
|
||||
---
|
||||
|
||||
<RootLayout>
|
||||
@@ -37,14 +26,16 @@ const posts =
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
gap: 16px;
|
||||
align-items: baseline;
|
||||
|
||||
li {
|
||||
padding: 1px 16px
|
||||
.container {
|
||||
background-color: var(--color-gray);
|
||||
}
|
||||
li a {
|
||||
color: var(--color-gray);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
li.selected-tag a {
|
||||
color: var(--color-gold);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,22 +80,13 @@ const posts =
|
||||
<ul class="tags">
|
||||
{
|
||||
tags.map(tag =>
|
||||
<li>
|
||||
{
|
||||
tag === selectedTag
|
||||
? <a href="/blog">
|
||||
<NotchedBox color="gray" fillNotches={"left"}>
|
||||
{tag}
|
||||
|
||||
</NotchedBox>
|
||||
</a>
|
||||
: <a href={`/blog/tag/${tag}`}>
|
||||
<NotchedBox color="gray" fillNotches={"none"}>
|
||||
{tag}
|
||||
</NotchedBox>
|
||||
</a>
|
||||
}
|
||||
</li>
|
||||
<li class={ tag === selectedTag ? "selected-tag" : "" }>
|
||||
<a href={`/blog/tag/${tag}`}>
|
||||
<NotchedBox color={ tag === selectedTag ? "gold" : "gray" } fillNotches={ tag === selectedTag ? "right" : "none"}>
|
||||
{tag}
|
||||
</NotchedBox>
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
|
||||
22
src/lib/blog.ts
Normal file
22
src/lib/blog.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { type CollectionEntry, getCollection } from "astro:content";
|
||||
|
||||
function publishedOnly(
|
||||
p: CollectionEntry<"blog">,
|
||||
): p is CollectionEntry<"blog"> & { data: { pubDate: Date } } {
|
||||
return p.data.pubDate !== undefined;
|
||||
}
|
||||
|
||||
export async function getPublishedPosts(
|
||||
tag?: string,
|
||||
): Promise<CollectionEntry<"blog">[]> {
|
||||
const allPosts = (await getCollection("blog", publishedOnly)).sort(
|
||||
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
||||
);
|
||||
return tag ? allPosts.filter((p) => p.data.tags.includes(tag)) : allPosts;
|
||||
}
|
||||
|
||||
export async function getAllTags(): Promise<string[]> {
|
||||
const posts = await getCollection("blog", publishedOnly);
|
||||
|
||||
return posts.flatMap((p) => p.data.tags || []);
|
||||
}
|
||||
37
src/pages/blog/tag/[...tag].rss.xml.ts
Normal file
37
src/pages/blog/tag/[...tag].rss.xml.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { type CollectionEntry, getCollection } from "astro:content";
|
||||
import type { AstroUserConfig } from "astro";
|
||||
import { SITE_TITLE, SITE_DESCRIPTION } from "../../../consts";
|
||||
import rss from "@astrojs/rss";
|
||||
import { getPublishedPosts } from "../../../lib/blog";
|
||||
|
||||
function publishedOnly(
|
||||
p: CollectionEntry<"blog">,
|
||||
): p is CollectionEntry<"blog"> & { data: { pubDate: Date } } {
|
||||
return p.data.pubDate !== undefined;
|
||||
}
|
||||
|
||||
export async function GET(context: AstroUserConfig) {
|
||||
const { tag: selectedTag } = context.params;
|
||||
const posts = await getPublishedPosts(selectedTag);
|
||||
|
||||
return rss({
|
||||
title: `${SITE_TITLE} - ${selectedTag}`,
|
||||
description: SITE_DESCRIPTION,
|
||||
site: context.site as string,
|
||||
items: posts.map((post) => ({
|
||||
...post.data,
|
||||
link: `/blog/${post.id}/`,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getCollection("blog", publishedOnly);
|
||||
|
||||
const tags = posts.flatMap((p) => p.data.tags);
|
||||
|
||||
return tags.map((tag) => ({
|
||||
params: { tag: tag },
|
||||
props: { tag: tag },
|
||||
}));
|
||||
}
|
||||
Reference in New Issue
Block a user