chore: cleanup, fixes, refactoring
This commit is contained in:
parent
7fbb752dec
commit
1220dd58f9
30 changed files with 698 additions and 145 deletions
7
package-lock.json
generated
7
package-lock.json
generated
|
@ -15,6 +15,7 @@
|
|||
"@tabler/icons-react": "^3.22.0",
|
||||
"astro": "^4.16.13",
|
||||
"luxon": "^3.5.0",
|
||||
"minisearch": "7.1.0",
|
||||
"rehype-prism-plus": "2.0.0",
|
||||
"rehype-stringify": "10.0.1",
|
||||
"remark": "15.0.1",
|
||||
|
@ -5832,6 +5833,12 @@
|
|||
"node": ">=16.13"
|
||||
}
|
||||
},
|
||||
"node_modules/minisearch": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.1.0.tgz",
|
||||
"integrity": "sha512-tv7c/uefWdEhcu6hvrfTihflgeEi2tN6VV7HJnCjK6VxM75QQJh4t9FwJCsA2EsRS8LCnu3W87CuGPWMocOLCA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mri": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"@tabler/icons-react": "^3.22.0",
|
||||
"astro": "^4.16.13",
|
||||
"luxon": "^3.5.0",
|
||||
"minisearch": "7.1.0",
|
||||
"rehype-prism-plus": "2.0.0",
|
||||
"rehype-stringify": "10.0.1",
|
||||
"remark": "15.0.1",
|
||||
|
|
|
@ -15,33 +15,65 @@ import YouTubePlayer from "@components/blocks//YouTubePlayer.astro";
|
|||
import { md } from "@utils/helpers/general.js";
|
||||
import { getPopularPosts } from "@utils/getPopularPosts.js";
|
||||
|
||||
const analytics = await fetchAnalyticsData();
|
||||
const links = await fetchLinks();
|
||||
const posts = await fetchAllPosts();
|
||||
const [analytics, links, posts] = await Promise.all([
|
||||
fetchAnalyticsData(),
|
||||
fetchLinks(),
|
||||
fetchAllPosts(),
|
||||
]);
|
||||
const popularPosts = getPopularPosts(posts, analytics);
|
||||
const { block } = Astro.props;
|
||||
const { blocks } = Astro.props;
|
||||
const processedBlocks = await Promise.all(
|
||||
blocks.map(async (block) => {
|
||||
if (block.type === "markdown") {
|
||||
return {
|
||||
...block,
|
||||
html: await md(block.text),
|
||||
};
|
||||
}
|
||||
if (block.type === "divider") {
|
||||
return {
|
||||
...block,
|
||||
html: await md(block.markup),
|
||||
};
|
||||
}
|
||||
return block;
|
||||
})
|
||||
);
|
||||
|
||||
---
|
||||
|
||||
{
|
||||
block.type === "addon_links" && (
|
||||
<AddonLinks popularPosts={popularPosts} links={links} />
|
||||
)
|
||||
}
|
||||
<div>
|
||||
{
|
||||
processedBlocks.map((block) => (
|
||||
<>
|
||||
{block.type === "addon_links" && (
|
||||
<AddonLinks popularPosts={popularPosts} links={links} />
|
||||
)}
|
||||
|
||||
{block.type === "associated_media" && <AssociatedMedia media={block.media} />}
|
||||
{block.type === "associated_media" && (
|
||||
<AssociatedMedia media={block.media} />
|
||||
)}
|
||||
|
||||
{block.type === "divider" && <div set:html={md(block.markup)} />}
|
||||
{block.type === "divider" && <div set:html={block.html} />}
|
||||
|
||||
{block.type === "github_banner" && <GitHub url={block.url} />}
|
||||
{block.type === "github_banner" && <GitHub url={block.url} />}
|
||||
|
||||
{block.type === "hero" && <Hero image={block.image} alt={block.alt} />}
|
||||
{block.type === "hero" && <Hero image={block.image} alt={block.alt} />}
|
||||
|
||||
{block.type === "markdown" && <div set:html={md(block.text)} />}
|
||||
{block.type === "markdown" && <div set:html={block.text} />}
|
||||
|
||||
{block.type === "npm_banner" && <Npm url={block.url} command={block.command} />}
|
||||
{block.type === "npm_banner" && (
|
||||
<Npm url={block.url} command={block.command} />
|
||||
)}
|
||||
|
||||
{block.type === "modal" && <Modal content={block.content} />}
|
||||
{block.type === "modal" && <Modal content={block.content} />}
|
||||
|
||||
{block.type === "rss_banner" && <Rss url={block.url} text={block.text} />}
|
||||
{block.type === "rss_banner" && (
|
||||
<Rss url={block.url} text={block.text} />
|
||||
)}
|
||||
|
||||
{block.type === "youtube_player" && <YouTubePlayer url={block.url} />}
|
||||
{block.type === "youtube_player" && <YouTubePlayer url={block.url} />}
|
||||
</>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
|
|
@ -8,12 +8,13 @@ import { IconActivity } from "@tabler/icons-react";
|
|||
|
||||
import Rss from "@components/blocks/banners/Rss.astro";
|
||||
|
||||
const music = await fetchMusicData();
|
||||
const tv = await fetchShows();
|
||||
const movies = await fetchMovies();
|
||||
const books = await fetchBooks();
|
||||
const links = await fetchLinks();
|
||||
|
||||
const [music, tv, movies, books, links] = await Promise.all([
|
||||
fetchMusicData(),
|
||||
fetchShows(),
|
||||
fetchMovies(),
|
||||
fetchBooks(),
|
||||
fetchLinks(),
|
||||
]);
|
||||
const track = music.week?.tracks[0];
|
||||
const show = tv.recentlyWatched[0];
|
||||
const movie = movies.recentlyWatched[0];
|
||||
|
@ -34,7 +35,8 @@ const link = links[0];
|
|||
<li>
|
||||
<span class="tv">Last episode watched:</span>
|
||||
<strong class="highlight-text">{show.formatted_episode}</strong> of <a
|
||||
href={show.url}>{show.title}</a>
|
||||
href={show.url}>{show.title}</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="movies">Last movie watched:</span>
|
||||
|
|
|
@ -5,8 +5,10 @@ export async function onRequest(context, next) {
|
|||
const { locals } = context;
|
||||
|
||||
try {
|
||||
const globals = await fetchGlobals();
|
||||
const nav = await fetchNavigation();
|
||||
const [globals, nav] = await Promise.all([
|
||||
fetchGlobals(),
|
||||
fetchNavigation(),
|
||||
]);
|
||||
|
||||
locals.globals = globals;
|
||||
locals.nav = nav;
|
||||
|
|
|
@ -4,7 +4,6 @@ import BlockRenderer from "@components/blocks/BlockRenderer.astro";
|
|||
import { fetchPages } from "@utils/data/pages.js";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const pages = await fetchPages();
|
||||
return pages.map((page) => ({
|
||||
|
@ -23,5 +22,5 @@ const currentUrl = Astro.url.pathname;
|
|||
ogImage={page.open_graph_image}
|
||||
currentUrl={currentUrl}
|
||||
>
|
||||
{page.blocks.map((block) => <BlockRenderer block={block} />)}
|
||||
<BlockRenderer blocks={page.blocks} />
|
||||
</Layout>
|
||||
|
|
|
@ -5,29 +5,30 @@ import ProgressBar from "@components/media/ProgressBar.astro";
|
|||
import { fetchBooks } from "@utils/data/books.js";
|
||||
import { fetchGlobalData } from "@utils/data/global/index.js";
|
||||
import { md, htmlTruncate } from "@utils/helpers/general.js";
|
||||
import { bookYearLinks } from "@utils/helpers/media.js";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
const books = await fetchBooks();
|
||||
const currentBookCount = books.currentYear.length;
|
||||
const bookData = books.all
|
||||
.filter((book) => book.status === "started")
|
||||
.reverse();
|
||||
const processedBooks = await Promise.all(
|
||||
bookData.map(async (book) => {
|
||||
const descriptionHtml = await md(book.description);
|
||||
const truncatedHtml = htmlTruncate(descriptionHtml, 50);
|
||||
return {
|
||||
...book,
|
||||
truncatedDescription: truncatedHtml,
|
||||
};
|
||||
})
|
||||
);
|
||||
const { globals } = await fetchGlobalData(Astro);
|
||||
const title = "Currently reading";
|
||||
const description = "Here's what I'm reading at the moment.";
|
||||
const updated = new Date().toISOString();
|
||||
const currentYear = new Date().getFullYear();
|
||||
const bookData = books.all
|
||||
.filter((book) => book.status === "started")
|
||||
.reverse();
|
||||
const currentBookCount = books.currentYear.length;
|
||||
const bookYearLinks = (years) =>
|
||||
years
|
||||
.sort((a, b) => b.value - a.value)
|
||||
.map(
|
||||
(year, index) =>
|
||||
`<a href="/books/years/${year.value}">${year.value}</a>${
|
||||
index < years.length - 1 ? " / " : ""
|
||||
}`
|
||||
)
|
||||
.join("");
|
||||
---
|
||||
|
||||
<Layout
|
||||
|
@ -49,7 +50,7 @@ const bookYearLinks = (years) =>
|
|||
/>
|
||||
<hr />
|
||||
{
|
||||
bookData.map((book) => (
|
||||
processedBooks.map((book) => (
|
||||
<article class="book-entry" key={book.url}>
|
||||
<a href={book.url}>
|
||||
<img
|
||||
|
@ -77,7 +78,7 @@ const bookYearLinks = (years) =>
|
|||
{book.description && (
|
||||
<div
|
||||
class="description"
|
||||
set:html={htmlTruncate(md(book.description))}
|
||||
set:html={book.truncatedDescription}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
|||
import { fetchActivity } from "@utils/data/activity.js";
|
||||
|
||||
export async function GET() {
|
||||
const globals = await fetchGlobals();
|
||||
const activity = await fetchActivity();
|
||||
|
||||
const [globals, activity] = await Promise.all([
|
||||
fetchGlobals(),
|
||||
fetchActivity(),
|
||||
]);
|
||||
const feed = generateJsonFeed({
|
||||
permalink: "/feeds/all.json",
|
||||
title: "All activity / Cory Dransfeldt",
|
||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
|||
import { fetchActivity } from "@utils/data/activity.js";
|
||||
|
||||
export async function GET() {
|
||||
const globals = await fetchGlobals();
|
||||
const activity = await fetchActivity();
|
||||
|
||||
const [globals, activity] = await Promise.all([
|
||||
fetchGlobals(),
|
||||
fetchActivity(),
|
||||
]);
|
||||
const rss = generateRssFeed({
|
||||
permalink: "/feeds/all.xml",
|
||||
title: "All activity feed",
|
||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
|||
import { fetchBooks } from "@utils/data/books.js";
|
||||
|
||||
export async function GET() {
|
||||
const globals = await fetchGlobals();
|
||||
const books = await fetchBooks();
|
||||
|
||||
const [globals, books] = await Promise.all([
|
||||
fetchGlobals(),
|
||||
fetchBooks(),
|
||||
]);
|
||||
const feed = generateJsonFeed({
|
||||
permalink: "/feeds/books.json",
|
||||
title: "Books / Cory Dransfeldt",
|
||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
|||
import { fetchBooks } from "@utils/data/books.js";
|
||||
|
||||
export async function GET() {
|
||||
const globals = await fetchGlobals();
|
||||
const books = await fetchBooks();
|
||||
|
||||
const [globals, books] = await Promise.all([
|
||||
fetchGlobals(),
|
||||
fetchBooks(),
|
||||
]);
|
||||
const rss = generateRssFeed({
|
||||
permalink: "/feeds/books.xml",
|
||||
title: "Books feed",
|
||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
|||
import { fetchLinks } from "@utils/data/links.js";
|
||||
|
||||
export async function GET() {
|
||||
const globals = await fetchGlobals();
|
||||
const links = await fetchLinks();
|
||||
|
||||
const [globals, links] = await Promise.all([
|
||||
fetchGlobals(),
|
||||
fetchLinks(),
|
||||
]);
|
||||
const feed = generateJsonFeed({
|
||||
permalink: "/feeds/links.json",
|
||||
title: "Links / Cory Dransfeldt",
|
||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
|||
import { fetchLinks } from "@utils/data/links";
|
||||
|
||||
export async function GET() {
|
||||
const globals = await fetchGlobals();
|
||||
const links = await fetchLinks();
|
||||
|
||||
const [globals, links] = await Promise.all([
|
||||
fetchGlobals(),
|
||||
fetchLinks(),
|
||||
]);
|
||||
const rss = generateRssFeed({
|
||||
permalink: "/feeds/links.xml",
|
||||
title: "Links feed",
|
||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from '@utils/data/globals.js';
|
|||
import { fetchMovies } from '@utils/data/movies';
|
||||
|
||||
export async function GET() {
|
||||
const globals = await fetchGlobals();
|
||||
const movies = await fetchMovies();
|
||||
|
||||
const [globals, movies] = await Promise.all([
|
||||
fetchGlobals(),
|
||||
fetchMovies(),
|
||||
]);
|
||||
const feed = generateJsonFeed({
|
||||
permalink: "/feeds/movies.json",
|
||||
title: "Movies / Cory Dransfeldt",
|
||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
|||
import { fetchMovies } from "@utils/data/movies.js";
|
||||
|
||||
export async function GET() {
|
||||
const globals = await fetchGlobals();
|
||||
const movies = await fetchMovies();
|
||||
|
||||
const [globals, movies] = await Promise.all([
|
||||
fetchGlobals(),
|
||||
fetchMovies(),
|
||||
]);
|
||||
const rss = generateRssFeed({
|
||||
permalink: "/feeds/movies.xml",
|
||||
title: "Movies feed",
|
||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
|||
import { fetchAllPosts } from "@utils/data/posts.js";
|
||||
|
||||
export async function GET() {
|
||||
const globals = await fetchGlobals();
|
||||
const posts = await fetchAllPosts();
|
||||
|
||||
const [globals, posts] = await Promise.all([
|
||||
fetchGlobals(),
|
||||
fetchAllPosts(),
|
||||
]);
|
||||
const feed = generateJsonFeed({
|
||||
permalink: "/feeds/posts.json",
|
||||
title: "Posts / Cory Dransfeldt",
|
||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
|||
import { fetchAllPosts } from "@utils/data/posts.js";
|
||||
|
||||
export async function GET() {
|
||||
const globals = await fetchGlobals();
|
||||
const posts = await fetchAllPosts();
|
||||
|
||||
const [globals, posts] = await Promise.all([
|
||||
fetchGlobals(),
|
||||
fetchAllPosts(),
|
||||
]);
|
||||
const rss = generateRssFeed({
|
||||
permalink: "/feeds/posts.xml",
|
||||
title: "Posts feed",
|
||||
|
|
|
@ -45,9 +45,10 @@ const generateSyndicationRSS = async (globals, entries) => {
|
|||
|
||||
export async function GET() {
|
||||
try {
|
||||
const globals = await fetchGlobals();
|
||||
const entries = await fetchSyndication();
|
||||
|
||||
const [globals, entries] = await Promise.all([
|
||||
fetchGlobals(),
|
||||
fetchSyndication(),
|
||||
]);
|
||||
const rss = await generateSyndicationRSS(globals, entries);
|
||||
|
||||
return new Response(rss, {
|
||||
|
|
|
@ -1,48 +1,53 @@
|
|||
---
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Paginator from "@components/nav/Paginator.astro";
|
||||
import RssBanner from "@components/blocks/banners/Rss.astro";
|
||||
import Rss from "@components/blocks/banners/Rss.astro";
|
||||
import { fetchLinks } from "@utils/data/links.js";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
const links = await fetchLinks();
|
||||
const title = "Links";
|
||||
const description =
|
||||
"These are links I've liked or otherwise found interesting. They're all added manually, after having been read and, I suppose, properly considered.";
|
||||
const pageSize = 30;
|
||||
const currentPage = parseInt(Astro.url.searchParams.get("page") || "1", 10);
|
||||
const totalPages = Math.ceil(links.length / pageSize);
|
||||
const paginatedLinks = links.slice(
|
||||
(currentPage - 1) * pageSize,
|
||||
currentPage * pageSize
|
||||
);
|
||||
export const getStaticPaths = async ({ paginate }) => {
|
||||
const links = await fetchLinks();
|
||||
return paginate(links, {
|
||||
pageSize: 30,
|
||||
});
|
||||
};
|
||||
|
||||
const { page } = Astro.props;
|
||||
|
||||
const paginatedLinks = page.data;
|
||||
const pagination = {
|
||||
currentPage,
|
||||
totalPages,
|
||||
hasPrevious: currentPage > 1,
|
||||
hasNext: currentPage < totalPages,
|
||||
previousPage: currentPage > 1 ? `/links?page=${currentPage - 1}` : null,
|
||||
nextPage: currentPage < totalPages ? `/links?page=${currentPage + 1}` : null,
|
||||
pages: Array.from({ length: totalPages }, (_, index) => ({
|
||||
number: index + 1,
|
||||
href: `/links?page=${index + 1}`,
|
||||
currentPage: page.currentPage,
|
||||
totalPages: page.lastPage,
|
||||
hasPrevious: page.currentPage > 1,
|
||||
hasNext: page.currentPage < page.lastPage,
|
||||
previousPage: page.url.prev || null,
|
||||
nextPage: page.url.next || null,
|
||||
pages: Array.from({ length: page.lastPage }, (_, i) => ({
|
||||
number: i + 1,
|
||||
href: i === 0 ? `/links` : `/links/${i + 1}`,
|
||||
})),
|
||||
};
|
||||
|
||||
const pageTitle =
|
||||
pagination.currentPage === 1
|
||||
? "Linls"
|
||||
: `Links / page ${pagination.currentPage}`;
|
||||
const description =
|
||||
"These are links I've liked or otherwise found interesting. They're all added manually, after having been read and, I suppose, properly considered.";
|
||||
---
|
||||
|
||||
<Layout
|
||||
pageTitle={title}
|
||||
pageTitle={pageTitle}
|
||||
description={description}
|
||||
currentUrl={Astro.url.pathname}
|
||||
>
|
||||
{
|
||||
currentPage === 1 && (
|
||||
pagination.currentPage === 1 && (
|
||||
<>
|
||||
<h2 class="page-title">{title}</h2>
|
||||
<h2 class="page-title">{pageTitle}</h2>
|
||||
<p>{description}</p>
|
||||
<RssBanner
|
||||
<Rss
|
||||
url="/feeds/links"
|
||||
text="Subscribe to my links feed or follow along on this page"
|
||||
/>
|
||||
|
@ -50,7 +55,6 @@ const pagination = {
|
|||
</>
|
||||
)
|
||||
}
|
||||
|
||||
<div class="link-grid">
|
||||
{
|
||||
paginatedLinks.map((link) => (
|
||||
|
@ -68,6 +72,5 @@ const pagination = {
|
|||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
<Paginator pagination={pagination} />
|
||||
</Layout>
|
|
@ -19,8 +19,10 @@ import { mediaLinks } from "@utils/helpers/media.js";
|
|||
export const prerender = true;
|
||||
|
||||
const { globals } = await fetchGlobalData(Astro);
|
||||
const music = await fetchMusicData();
|
||||
const albumReleases = await fetchAlbumReleases();
|
||||
const [music, albumReleases ] = await Promise.all([
|
||||
fetchMusicData(),
|
||||
fetchAlbumReleases(),
|
||||
]);
|
||||
|
||||
const title = "Music";
|
||||
const description =
|
||||
|
|
|
@ -5,46 +5,45 @@ import Layout from "@layouts/Layout.astro";
|
|||
import Paginator from "@components/nav/Paginator.astro";
|
||||
import { md } from "@utils/helpers/general.js";
|
||||
import { DateTime } from "luxon";
|
||||
import type { GetStaticPaths, Page } from "astro";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const pageSize = 15; // Declare inside this function
|
||||
export const getStaticPaths = async ({ paginate }) => {
|
||||
const posts = await fetchAllPosts();
|
||||
const sortedPosts = posts.sort((a, b) => new Date(b.date) - new Date(a.date));
|
||||
|
||||
const totalPages = Math.ceil(posts.length / pageSize);
|
||||
const paths = Array.from({ length: totalPages }, (_, i) => ({
|
||||
params: { page: (i + 1).toString() },
|
||||
}));
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
const pageSize = 15;
|
||||
const currentUrl = Astro.url.pathname;
|
||||
const currentPage = Astro.params.page ? parseInt(Astro.params.page, 10) : 1;
|
||||
const posts = await fetchAllPosts();
|
||||
const totalPosts = posts.length;
|
||||
const totalPages = Math.ceil(totalPosts / pageSize);
|
||||
const start = (currentPage - 1) * pageSize;
|
||||
const end = start + pageSize;
|
||||
const paginatedPosts = posts.slice(start, end);
|
||||
return paginate(sortedPosts, {
|
||||
pageSize: 15,
|
||||
});
|
||||
};
|
||||
|
||||
const { page } = Astro.props;
|
||||
const paginatedPosts = page.data;
|
||||
const pagination = {
|
||||
currentPage,
|
||||
totalPages,
|
||||
hasPrevious: currentPage > 1,
|
||||
hasNext: currentPage < totalPages,
|
||||
previousPage: currentPage > 1 ? `/posts/${currentPage - 1}` : null,
|
||||
nextPage: currentPage < totalPages ? `/posts/${currentPage + 1}` : null,
|
||||
pages: Array.from({ length: totalPages }, (_, i) => ({
|
||||
currentPage: page.currentPage,
|
||||
totalPages: page.lastPage,
|
||||
hasPrevious: page.currentPage > 1,
|
||||
hasNext: page.currentPage < page.lastPage,
|
||||
previousPage: page.url.prev || null,
|
||||
nextPage: page.url.next || null,
|
||||
pages: Array.from({ length: page.lastPage }, (_, i) => ({
|
||||
number: i + 1,
|
||||
href: `/posts/${i + 1}`,
|
||||
href: i === 0 ? `/posts` : `/posts/${i + 1}`,
|
||||
})),
|
||||
};
|
||||
const pageTitle =
|
||||
pagination.currentPage === 1
|
||||
? "Posts"
|
||||
: `Posts / page ${pagination.currentPage}`;
|
||||
const description =
|
||||
"These are posts I've written. They're all added manually, after having been written and, I suppose, properly considered.";
|
||||
---
|
||||
|
||||
<Layout pageTitle="All posts" currentUrl={currentUrl}>
|
||||
<Layout
|
||||
pageTitle={pageTitle}
|
||||
description={description}
|
||||
currentUrl={Astro.url.pathname}
|
||||
>
|
||||
{
|
||||
paginatedPosts.map((post) => (
|
||||
<article>
|
||||
|
@ -61,6 +60,5 @@ const pagination = {
|
|||
</article>
|
||||
))
|
||||
}
|
||||
|
||||
<Paginator pagination={pagination} />
|
||||
</Layout>
|
||||
|
|
|
@ -7,9 +7,11 @@ import { fetchGlobalData } from "@utils/data/global/index.js";
|
|||
import { md } from "@utils/helpers/general.js";
|
||||
import { getPopularPosts } from "@utils/getPopularPosts.js";
|
||||
|
||||
const analytics = await fetchAnalyticsData();
|
||||
const links = await fetchLinks();
|
||||
const posts = await fetchAllPosts();
|
||||
const [analytics, links, posts] = await Promise.all([
|
||||
fetchAnalyticsData(),
|
||||
fetchLinks(),
|
||||
fetchAllPosts(),
|
||||
]);
|
||||
const popularPosts = getPopularPosts(posts, analytics);
|
||||
|
||||
import AddonLinks from "@components/blocks/links/AddonLinks.astro";
|
||||
|
@ -91,10 +93,7 @@ const htmlContent = md(post.content);
|
|||
)
|
||||
}
|
||||
<div set:html={htmlContent} />
|
||||
{
|
||||
post.blocks &&
|
||||
post.blocks.map((block) => <BlockRenderer block={block} />)
|
||||
}
|
||||
{post.blocks && <BlockRenderer blocks={post.blocks} />}
|
||||
{post.mastodon_url && <Mastodon url={post.mastodon_url} />}
|
||||
<AssociatedMedia
|
||||
artists={post.artists}
|
||||
|
|
93
src/pages/search.astro
Normal file
93
src/pages/search.astro
Normal file
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
import "@npm/minisearch/dist/es/index.js";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import AddonLinks from "@components/blocks/links/AddonLinks.astro";
|
||||
import { getPopularPosts } from "@utils/getPopularPosts.js";
|
||||
import { fetchAllPosts } from "@data/posts.js";
|
||||
import { fetchAnalyticsData } from "@data/analytics.js";
|
||||
import { fetchLinks } from "@data/links.js";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
const [analytics, links, posts] = await Promise.all([
|
||||
fetchAnalyticsData(),
|
||||
fetchLinks(),
|
||||
fetchAllPosts(),
|
||||
]);
|
||||
const popularPosts = getPopularPosts(posts, analytics);
|
||||
const title = "Search";
|
||||
const description = "Search for posts, links, artists, genres, movies, shows and books on my site.";
|
||||
---
|
||||
|
||||
<Layout
|
||||
pageTitle={title}
|
||||
description={description}
|
||||
currentUrl={Astro.url.pathname}
|
||||
>
|
||||
<h2 class="page-title">Search</h2>
|
||||
<p>
|
||||
You can find <a href="/posts">posts</a>, <a href="/links">links</a>, <a
|
||||
href="/music/#artists">artists</a
|
||||
>, genres, <a href="/watching#movies">movies</a>, <a href="/watching#tv"
|
||||
>shows</a
|
||||
> and <a href="/books">books</a> via the field below (though it only surfaces
|
||||
movies and shows I've watched and books I've written something about).
|
||||
</p>
|
||||
<noscript>
|
||||
<p>
|
||||
<strong class="highlight-text"
|
||||
>If you're seeing this it means that you've (quite likely) disabled
|
||||
JavaScript (that's a totally valid choice!).</strong
|
||||
> You can search for anything on my site using the form below, but your query
|
||||
will be routed through <a href="https://duckduckgo.com">DuckDuckGo</a>.
|
||||
</p>
|
||||
<p>
|
||||
<strong class="highlight-text">Type something in and hit enter.</strong>
|
||||
</p>
|
||||
</noscript>
|
||||
<form class="search__form" action="https://duckduckgo.com" method="get">
|
||||
<input
|
||||
class="search__form--input"
|
||||
placeholder="Search"
|
||||
type="search"
|
||||
name="q"
|
||||
autocomplete="off"
|
||||
autofocus
|
||||
/>
|
||||
<details>
|
||||
<summary class="highlight-text">Filter by type</summary>
|
||||
<fieldset class="search__form--type">
|
||||
<label
|
||||
><input type="checkbox" name="type" value="post" checked /> Posts</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" name="type" value="link" checked /> Links</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" name="type" value="artist" checked /> Artists</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" name="type" value="genre" checked /> Genres</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" name="type" value="book" checked /> Books</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" name="type" value="movie" checked /> Movies</label
|
||||
>
|
||||
<label
|
||||
><input type="checkbox" name="type" value="show" checked /> Shows</label
|
||||
>
|
||||
</fieldset>
|
||||
</details>
|
||||
<input
|
||||
class="search__form--fallback"
|
||||
type="hidden"
|
||||
name="sites"
|
||||
value="coryd.dev"
|
||||
/>
|
||||
</form>
|
||||
<ul class="search__results"></ul>
|
||||
<button class="search__load-more" style="display:none">Load More</button>
|
||||
<AddonLinks popularPosts={popularPosts} links={links} />
|
||||
</Layout>
|
60
src/pages/watching/favorite-movies.astro
Normal file
60
src/pages/watching/favorite-movies.astro
Normal file
|
@ -0,0 +1,60 @@
|
|||
---
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Grid from "@components/media/Grid.astro";
|
||||
import { IconArrowLeft } from "@tabler/icons-react";
|
||||
import { fetchMovies } from "@utils/data/movies.js";
|
||||
import { fetchGlobalData } from "@utils/data/global/index.js";
|
||||
import { shuffleArray } from "@utils/helpers/general.js";
|
||||
|
||||
const { globals } = await fetchGlobalData(Astro);
|
||||
const movies = await fetchMovies();
|
||||
const favoriteMovies = movies.favorites;
|
||||
const pageSize = 24;
|
||||
const currentPage = Astro.params.page ? parseInt(Astro.params.page, 10) : 1;
|
||||
const totalMovies = favoriteMovies.length;
|
||||
const totalPages = Math.ceil(totalMovies / pageSize);
|
||||
const start = (currentPage - 1) * pageSize;
|
||||
const end = start + pageSize;
|
||||
const paginatedMovies = favoriteMovies.slice(start, end);
|
||||
|
||||
const pagination = {
|
||||
currentPage,
|
||||
totalPages,
|
||||
hasPrevious: currentPage > 1,
|
||||
hasNext: currentPage < totalPages,
|
||||
previousPage: currentPage > 1 ? `/watching/favorite-movies/${currentPage - 1}` : null,
|
||||
nextPage: currentPage < totalPages ? `/watching/favorite-movies/${currentPage + 1}` : null,
|
||||
pages: Array.from({ length: totalPages }, (_, i) => ({
|
||||
number: i + 1,
|
||||
href: `/watching/favorite-movies/${i + 1}`,
|
||||
})),
|
||||
};
|
||||
|
||||
const pageTitle = currentPage === 1 ? "Favorite Movies" : `Favorite Movies - Page ${currentPage}`;
|
||||
const description = "These are my favorite movies. There are many like them, but these are mine.";
|
||||
---
|
||||
|
||||
<Layout
|
||||
pageTitle={pageTitle}
|
||||
description={description}
|
||||
fullUrl={Astro.url.pathname}
|
||||
ogImage={shuffleArray(favoriteMovies)[0].backdrop}
|
||||
>
|
||||
<a href="/watching" class="back-link">
|
||||
<IconArrowLeft size={18} /> Back to watching
|
||||
</a>
|
||||
{currentPage === 1 && (
|
||||
<>
|
||||
<h2 class="page-title">Favorite Movies</h2>
|
||||
<p>These are my favorite movies. There are many like them, but these are mine.</p>
|
||||
<hr />
|
||||
</>
|
||||
)}
|
||||
<Grid
|
||||
data={paginatedMovies}
|
||||
pagination={pagination}
|
||||
shape="poster"
|
||||
count={pageSize}
|
||||
loading="eager"
|
||||
/>
|
||||
</Layout>
|
60
src/pages/watching/favorite-shows.astro
Normal file
60
src/pages/watching/favorite-shows.astro
Normal file
|
@ -0,0 +1,60 @@
|
|||
---
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Grid from "@components/media/Grid.astro";
|
||||
import { IconArrowLeft } from "@tabler/icons-react";
|
||||
import { fetchShows } from "@utils/data/tv.js";
|
||||
import { fetchGlobalData } from "@utils/data/global/index.js";
|
||||
import { shuffleArray } from "@utils/helpers/general.js";
|
||||
|
||||
const { globals } = await fetchGlobalData(Astro);
|
||||
const shows = await fetchShows();
|
||||
const favoriteShows = shows.favorites;
|
||||
const pageSize = 24;
|
||||
const currentPage = Astro.params.page ? parseInt(Astro.params.page, 10) : 1;
|
||||
const totalTvShows = favoriteShows.length;
|
||||
const totalPages = Math.ceil(totalTvShows / pageSize);
|
||||
const start = (currentPage - 1) * pageSize;
|
||||
const end = start + pageSize;
|
||||
const paginatedTvShows = favoriteShows.slice(start, end);
|
||||
|
||||
const pagination = {
|
||||
currentPage,
|
||||
totalPages,
|
||||
hasPrevious: currentPage > 1,
|
||||
hasNext: currentPage < totalPages,
|
||||
previousPage: currentPage > 1 ? `/watching/favorite-shows/${currentPage - 1}` : null,
|
||||
nextPage: currentPage < totalPages ? `/watching/favorite-shows/${currentPage + 1}` : null,
|
||||
pages: Array.from({ length: totalPages }, (_, i) => ({
|
||||
number: i + 1,
|
||||
href: `/watching/favorite-shows/${i + 1}`,
|
||||
})),
|
||||
};
|
||||
|
||||
const pageTitle = currentPage === 1 ? "Favorite Shows" : `Favorite shows / page ${currentPage}`;
|
||||
const description = "These are my favorite TV shows. There are many like them, but these are mine.";
|
||||
---
|
||||
|
||||
<Layout
|
||||
pageTitle={pageTitle}
|
||||
description={description}
|
||||
fullUrl={Astro.url.pathname}
|
||||
ogImage={shuffleArray(favoriteShows)[0].backdrop}
|
||||
>
|
||||
<a href="/watching" class="back-link">
|
||||
<IconArrowLeft size={18} /> Back to watching
|
||||
</a>
|
||||
{currentPage === 1 && (
|
||||
<>
|
||||
<h2 class="page-title">Favorite Shows</h2>
|
||||
<p>These are my favorite TV shows. There are many like them, but these are mine.</p>
|
||||
<hr />
|
||||
</>
|
||||
)}
|
||||
<Grid
|
||||
data={paginatedTvShows}
|
||||
pagination={pagination}
|
||||
shape="poster"
|
||||
count={pageSize}
|
||||
loading="eager"
|
||||
/>
|
||||
</Layout>
|
120
src/pages/watching/movies/[slug].astro
Normal file
120
src/pages/watching/movies/[slug].astro
Normal file
|
@ -0,0 +1,120 @@
|
|||
---
|
||||
import { DateTime } from "luxon";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import AssociatedMedia from "@components/blocks/AssociatedMedia.astro";
|
||||
import Warning from "@components/blocks/banners/Warning.astro";
|
||||
import {
|
||||
IconArrowLeft,
|
||||
IconHeart,
|
||||
IconNeedle,
|
||||
IconCircleCheck,
|
||||
} from "@tabler/icons-react";
|
||||
import { fetchGlobalData } from "@utils/data/global/index.js";
|
||||
import { fetchMovieByUrl } from "@utils/data/dynamic/movieByUrl.js";
|
||||
|
||||
const { globals } = await fetchGlobalData(Astro);
|
||||
const movie = await fetchMovieByUrl(Astro.url.pathname);
|
||||
|
||||
if (!movie) return Astro.redirect("/404", 404);
|
||||
|
||||
const pageTitle = `${movie.title} / Movies`;
|
||||
const description = movie.description || `Details about ${movie.title}.`;
|
||||
const alt = `${movie.title} / ${movie.year}${movie.rating ? ` (${movie.rating})` : ""}`;
|
||||
---
|
||||
|
||||
<Layout
|
||||
pageTitle={pageTitle}
|
||||
description={description}
|
||||
fullUrl={Astro.url.pathname}
|
||||
ogImage={movie.backdrop}
|
||||
>
|
||||
<a class="back-link" href="/watching" title="Go back to the watching index page">
|
||||
<IconArrowLeft size={18} /> Back to watching
|
||||
</a>
|
||||
<article class="watching focus">
|
||||
<img
|
||||
srcset={`
|
||||
${globals.cdn_url}${movie.backdrop}?class=bannersm&type=webp 256w,
|
||||
${globals.cdn_url}${movie.backdrop}?class=bannermd&type=webp 512w,
|
||||
${globals.cdn_url}${movie.backdrop}?class=bannerbase&type=webp 1024w
|
||||
`}
|
||||
sizes="(max-width: 450px) 256px,
|
||||
(max-width: 850px) 512px,
|
||||
1024px"
|
||||
src={`${globals.cdn_url}${movie.backdrop}?class=bannersm&type=webp`}
|
||||
alt={alt}
|
||||
class="image-banner"
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
width="256"
|
||||
height="180"
|
||||
/>
|
||||
<div class="media-meta">
|
||||
<span class="title">
|
||||
<strong>{movie.title}</strong>
|
||||
{movie.year && !movie.rating && ` (${movie.year})`}
|
||||
</span>
|
||||
{
|
||||
movie.rating && (
|
||||
<span>
|
||||
{movie.rating}
|
||||
{movie.year && ` (${movie.year})`}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
{
|
||||
movie.favorite && (
|
||||
<span class="sub-meta favorite">
|
||||
<IconHeart size={18} /> This is one of my favorite movies!
|
||||
</span>
|
||||
)
|
||||
}
|
||||
{
|
||||
movie.tattoo && (
|
||||
<span class="sub-meta tattoo">
|
||||
<IconNeedle size={18} /> I have a tattoo inspired by this movie!
|
||||
</span>
|
||||
)
|
||||
}
|
||||
{
|
||||
movie.collected && (
|
||||
<span class="sub-meta collected">
|
||||
<IconCircleCheck size={18} /> This movie is in my collection!
|
||||
</span>
|
||||
)
|
||||
}
|
||||
{
|
||||
movie.lastWatched && (
|
||||
<span class="sub-meta">
|
||||
Last watched on{" "}
|
||||
{DateTime.fromISO(movie.lastWatched).toLocaleString(DateTime.DATE_FULL)}.
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
{movie.review && (
|
||||
<>
|
||||
<h2>My thoughts</h2>
|
||||
<Warning text="There are probably spoilers after this banner — this is a warning about them." />
|
||||
<div set:html={movie.review}></div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<AssociatedMedia
|
||||
artists={movie.artists}
|
||||
books={movie.books}
|
||||
genres={movie.genres}
|
||||
movies={movie.related_movies}
|
||||
posts={movie.posts}
|
||||
shows={movie.shows}
|
||||
/>
|
||||
|
||||
{movie.description && (
|
||||
<>
|
||||
<h2>Overview</h2>
|
||||
<div set:html={movie.description}></div>
|
||||
</>
|
||||
)}
|
||||
</article>
|
||||
</Layout>
|
113
src/pages/watching/shows/[slug].astro
Normal file
113
src/pages/watching/shows/[slug].astro
Normal file
|
@ -0,0 +1,113 @@
|
|||
---
|
||||
import { DateTime } from "luxon";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import AssociatedMedia from "@components/blocks/AssociatedMedia.astro";
|
||||
import Warning from "@components/blocks/banners/Warning.astro";
|
||||
import {
|
||||
IconArrowLeft,
|
||||
IconHeart,
|
||||
IconNeedle,
|
||||
IconCircleCheck,
|
||||
} from "@tabler/icons-react";
|
||||
import { fetchGlobalData } from "@utils/data/global/index.js";
|
||||
import { fetchTvByUrl } from "@utils/data/dynamic/tvByUrl.js";
|
||||
|
||||
const { globals } = await fetchGlobalData(Astro);
|
||||
const show = await fetchTvByUrl(Astro.url.pathname);
|
||||
|
||||
if (!show) return Astro.redirect("/404", 404);
|
||||
|
||||
const pageTitle = `${show.title} / TV`;
|
||||
const description = show.description || `Details about ${show.title}.`;
|
||||
const alt = `${show.title} / ${show.year}`;
|
||||
---
|
||||
|
||||
<Layout
|
||||
pageTitle={pageTitle}
|
||||
description={description}
|
||||
fullUrl={Astro.url.pathname}
|
||||
ogImage={show.backdrop}
|
||||
>
|
||||
<a class="back-link" href="/watching" title="Go back to the watching index page">
|
||||
<IconArrowLeft size={18} /> Back to watching
|
||||
</a>
|
||||
<article class="watching focus">
|
||||
<img
|
||||
srcset={`
|
||||
${globals.cdn_url}${show.backdrop}?class=bannersm&type=webp 256w,
|
||||
${globals.cdn_url}${show.backdrop}?class=bannermd&type=webp 512w,
|
||||
${globals.cdn_url}${show.backdrop}?class=bannerbase&type=webp 1024w
|
||||
`}
|
||||
sizes="(max-width: 450px) 256px,
|
||||
(max-width: 850px) 512px,
|
||||
1024px"
|
||||
src={`${globals.cdn_url}${show.backdrop}?class=bannersm&type=webp`}
|
||||
alt={alt}
|
||||
class="image-banner"
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
width="256"
|
||||
height="180"
|
||||
/>
|
||||
<div class="media-meta">
|
||||
<span class="title">
|
||||
<strong>{show.title}</strong>
|
||||
{show.year && ` (${show.year})`}
|
||||
</span>
|
||||
{
|
||||
show.favorite && (
|
||||
<span class="sub-meta favorite">
|
||||
<IconHeart size={18} /> This is one of my favorite shows!
|
||||
</span>
|
||||
)
|
||||
}
|
||||
{
|
||||
show.tattoo && (
|
||||
<span class="sub-meta tattoo">
|
||||
<IconNeedle size={18} /> I have a tattoo inspired by this show!
|
||||
</span>
|
||||
)
|
||||
}
|
||||
{
|
||||
show.collected && (
|
||||
<span class="sub-meta collected">
|
||||
<IconCircleCheck size={18} /> This show is in my collection!
|
||||
</span>
|
||||
)
|
||||
}
|
||||
{
|
||||
show.episode?.formatted_episode && (
|
||||
<span class="sub-meta">
|
||||
I last watched{" "}
|
||||
<strong class="highlight-text">{show.episode.formatted_episode}</strong>{" "}
|
||||
on {DateTime.fromISO(show.episode.last_watched_at).toLocaleString(DateTime.DATE_FULL)}.
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
{show.review && (
|
||||
<>
|
||||
<h2>My thoughts</h2>
|
||||
<Warning text="There are probably spoilers after this banner — this is a warning about them." />
|
||||
<div set:html={show.review}></div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<AssociatedMedia
|
||||
artists={show.artists}
|
||||
books={show.books}
|
||||
genres={show.genres}
|
||||
movies={show.movies}
|
||||
posts={show.posts}
|
||||
shows={show.related_shows}
|
||||
/>
|
||||
|
||||
{show.description && (
|
||||
<>
|
||||
<h2>Overview</h2>
|
||||
<div set:html={show.description}></div>
|
||||
</>
|
||||
)}
|
||||
</article>
|
||||
</Layout>
|
25
src/utils/data/dynamic/movieByUrl.js
Normal file
25
src/utils/data/dynamic/movieByUrl.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { createClient } from "@supabase/supabase-js";
|
||||
import { removeTrailingSlash } from "@utils/helpers/general.js";
|
||||
|
||||
const SUPABASE_URL = import.meta.env.SUPABASE_URL;
|
||||
const SUPABASE_KEY = import.meta.env.SUPABASE_KEY;
|
||||
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
|
||||
|
||||
const movieCache = {};
|
||||
|
||||
export async function fetchMovieByUrl(url) {
|
||||
if (import.meta.env.MODE === "development" && movieCache[url]) return movieCache[url];
|
||||
|
||||
const { data: movie, error } = await supabase
|
||||
.from("optimized_movies")
|
||||
.select("*")
|
||||
.eq("url", removeTrailingSlash(url))
|
||||
.limit(1);
|
||||
if (error) {
|
||||
console.error("Error fetching movie:", error);
|
||||
return null;
|
||||
}
|
||||
if (import.meta.env.MODE === "development") movieCache[url] = movie[0];
|
||||
|
||||
return movie[0];
|
||||
}
|
25
src/utils/data/dynamic/tvByUrl.js
Normal file
25
src/utils/data/dynamic/tvByUrl.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { createClient } from "@supabase/supabase-js";
|
||||
import { removeTrailingSlash } from "@utils/helpers/general.js";
|
||||
|
||||
const SUPABASE_URL = import.meta.env.SUPABASE_URL;
|
||||
const SUPABASE_KEY = import.meta.env.SUPABASE_KEY;
|
||||
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
|
||||
|
||||
const tvCache = {};
|
||||
|
||||
export async function fetchTvByUrl(url) {
|
||||
if (import.meta.env.MODE === "development" && tvCache[url]) return tvCache[url];
|
||||
|
||||
const { data: tv, error } = await supabase
|
||||
.from("optimized_shows")
|
||||
.select("*")
|
||||
.eq("url", removeTrailingSlash(url))
|
||||
.limit(1);
|
||||
if (error) {
|
||||
console.error("Error fetching tv:", error);
|
||||
return null;
|
||||
}
|
||||
if (import.meta.env.MODE === "development") tvCache[url] = tv[0];
|
||||
|
||||
return tv[0];
|
||||
}
|
|
@ -71,7 +71,7 @@ export const escapeHtml = (str) =>
|
|||
|
||||
// urls
|
||||
export const encodeAmp = (url) => url.replace(/&/g, "&");
|
||||
export const removeTrailingSlash = (url) => url.replace(/\/$/, '');
|
||||
export const removeTrailingSlash = (url) => url.replace(/\/$/, "");
|
||||
|
||||
// dates
|
||||
export const dateToRFC822 = (date) =>
|
||||
|
|
Reference in a new issue