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",
|
"@tabler/icons-react": "^3.22.0",
|
||||||
"astro": "^4.16.13",
|
"astro": "^4.16.13",
|
||||||
"luxon": "^3.5.0",
|
"luxon": "^3.5.0",
|
||||||
|
"minisearch": "7.1.0",
|
||||||
"rehype-prism-plus": "2.0.0",
|
"rehype-prism-plus": "2.0.0",
|
||||||
"rehype-stringify": "10.0.1",
|
"rehype-stringify": "10.0.1",
|
||||||
"remark": "15.0.1",
|
"remark": "15.0.1",
|
||||||
|
@ -5832,6 +5833,12 @@
|
||||||
"node": ">=16.13"
|
"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": {
|
"node_modules/mri": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"@tabler/icons-react": "^3.22.0",
|
"@tabler/icons-react": "^3.22.0",
|
||||||
"astro": "^4.16.13",
|
"astro": "^4.16.13",
|
||||||
"luxon": "^3.5.0",
|
"luxon": "^3.5.0",
|
||||||
|
"minisearch": "7.1.0",
|
||||||
"rehype-prism-plus": "2.0.0",
|
"rehype-prism-plus": "2.0.0",
|
||||||
"rehype-stringify": "10.0.1",
|
"rehype-stringify": "10.0.1",
|
||||||
"remark": "15.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 { md } from "@utils/helpers/general.js";
|
||||||
import { getPopularPosts } from "@utils/getPopularPosts.js";
|
import { getPopularPosts } from "@utils/getPopularPosts.js";
|
||||||
|
|
||||||
const analytics = await fetchAnalyticsData();
|
const [analytics, links, posts] = await Promise.all([
|
||||||
const links = await fetchLinks();
|
fetchAnalyticsData(),
|
||||||
const posts = await fetchAllPosts();
|
fetchLinks(),
|
||||||
|
fetchAllPosts(),
|
||||||
|
]);
|
||||||
const popularPosts = getPopularPosts(posts, analytics);
|
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;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
{
|
<div>
|
||||||
block.type === "addon_links" && (
|
{
|
||||||
<AddonLinks popularPosts={popularPosts} links={links} />
|
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";
|
import Rss from "@components/blocks/banners/Rss.astro";
|
||||||
|
|
||||||
const music = await fetchMusicData();
|
const [music, tv, movies, books, links] = await Promise.all([
|
||||||
const tv = await fetchShows();
|
fetchMusicData(),
|
||||||
const movies = await fetchMovies();
|
fetchShows(),
|
||||||
const books = await fetchBooks();
|
fetchMovies(),
|
||||||
const links = await fetchLinks();
|
fetchBooks(),
|
||||||
|
fetchLinks(),
|
||||||
|
]);
|
||||||
const track = music.week?.tracks[0];
|
const track = music.week?.tracks[0];
|
||||||
const show = tv.recentlyWatched[0];
|
const show = tv.recentlyWatched[0];
|
||||||
const movie = movies.recentlyWatched[0];
|
const movie = movies.recentlyWatched[0];
|
||||||
|
@ -34,7 +35,8 @@ const link = links[0];
|
||||||
<li>
|
<li>
|
||||||
<span class="tv">Last episode watched:</span>
|
<span class="tv">Last episode watched:</span>
|
||||||
<strong class="highlight-text">{show.formatted_episode}</strong> of <a
|
<strong class="highlight-text">{show.formatted_episode}</strong> of <a
|
||||||
href={show.url}>{show.title}</a>
|
href={show.url}>{show.title}</a
|
||||||
|
>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<span class="movies">Last movie watched:</span>
|
<span class="movies">Last movie watched:</span>
|
||||||
|
|
|
@ -5,8 +5,10 @@ export async function onRequest(context, next) {
|
||||||
const { locals } = context;
|
const { locals } = context;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const globals = await fetchGlobals();
|
const [globals, nav] = await Promise.all([
|
||||||
const nav = await fetchNavigation();
|
fetchGlobals(),
|
||||||
|
fetchNavigation(),
|
||||||
|
]);
|
||||||
|
|
||||||
locals.globals = globals;
|
locals.globals = globals;
|
||||||
locals.nav = nav;
|
locals.nav = nav;
|
||||||
|
|
|
@ -4,7 +4,6 @@ import BlockRenderer from "@components/blocks/BlockRenderer.astro";
|
||||||
import { fetchPages } from "@utils/data/pages.js";
|
import { fetchPages } from "@utils/data/pages.js";
|
||||||
|
|
||||||
export const prerender = true;
|
export const prerender = true;
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const pages = await fetchPages();
|
const pages = await fetchPages();
|
||||||
return pages.map((page) => ({
|
return pages.map((page) => ({
|
||||||
|
@ -23,5 +22,5 @@ const currentUrl = Astro.url.pathname;
|
||||||
ogImage={page.open_graph_image}
|
ogImage={page.open_graph_image}
|
||||||
currentUrl={currentUrl}
|
currentUrl={currentUrl}
|
||||||
>
|
>
|
||||||
{page.blocks.map((block) => <BlockRenderer block={block} />)}
|
<BlockRenderer blocks={page.blocks} />
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
|
@ -5,29 +5,30 @@ import ProgressBar from "@components/media/ProgressBar.astro";
|
||||||
import { fetchBooks } from "@utils/data/books.js";
|
import { fetchBooks } from "@utils/data/books.js";
|
||||||
import { fetchGlobalData } from "@utils/data/global/index.js";
|
import { fetchGlobalData } from "@utils/data/global/index.js";
|
||||||
import { md, htmlTruncate } from "@utils/helpers/general.js";
|
import { md, htmlTruncate } from "@utils/helpers/general.js";
|
||||||
|
import { bookYearLinks } from "@utils/helpers/media.js";
|
||||||
|
|
||||||
export const prerender = true;
|
export const prerender = true;
|
||||||
|
|
||||||
const books = await fetchBooks();
|
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 { globals } = await fetchGlobalData(Astro);
|
||||||
const title = "Currently reading";
|
const title = "Currently reading";
|
||||||
const description = "Here's what I'm reading at the moment.";
|
const description = "Here's what I'm reading at the moment.";
|
||||||
const updated = new Date().toISOString();
|
const updated = new Date().toISOString();
|
||||||
const currentYear = new Date().getFullYear();
|
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
|
<Layout
|
||||||
|
@ -49,7 +50,7 @@ const bookYearLinks = (years) =>
|
||||||
/>
|
/>
|
||||||
<hr />
|
<hr />
|
||||||
{
|
{
|
||||||
bookData.map((book) => (
|
processedBooks.map((book) => (
|
||||||
<article class="book-entry" key={book.url}>
|
<article class="book-entry" key={book.url}>
|
||||||
<a href={book.url}>
|
<a href={book.url}>
|
||||||
<img
|
<img
|
||||||
|
@ -77,7 +78,7 @@ const bookYearLinks = (years) =>
|
||||||
{book.description && (
|
{book.description && (
|
||||||
<div
|
<div
|
||||||
class="description"
|
class="description"
|
||||||
set:html={htmlTruncate(md(book.description))}
|
set:html={book.truncatedDescription}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
||||||
import { fetchActivity } from "@utils/data/activity.js";
|
import { fetchActivity } from "@utils/data/activity.js";
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
const globals = await fetchGlobals();
|
const [globals, activity] = await Promise.all([
|
||||||
const activity = await fetchActivity();
|
fetchGlobals(),
|
||||||
|
fetchActivity(),
|
||||||
|
]);
|
||||||
const feed = generateJsonFeed({
|
const feed = generateJsonFeed({
|
||||||
permalink: "/feeds/all.json",
|
permalink: "/feeds/all.json",
|
||||||
title: "All activity / Cory Dransfeldt",
|
title: "All activity / Cory Dransfeldt",
|
||||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
||||||
import { fetchActivity } from "@utils/data/activity.js";
|
import { fetchActivity } from "@utils/data/activity.js";
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
const globals = await fetchGlobals();
|
const [globals, activity] = await Promise.all([
|
||||||
const activity = await fetchActivity();
|
fetchGlobals(),
|
||||||
|
fetchActivity(),
|
||||||
|
]);
|
||||||
const rss = generateRssFeed({
|
const rss = generateRssFeed({
|
||||||
permalink: "/feeds/all.xml",
|
permalink: "/feeds/all.xml",
|
||||||
title: "All activity feed",
|
title: "All activity feed",
|
||||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
||||||
import { fetchBooks } from "@utils/data/books.js";
|
import { fetchBooks } from "@utils/data/books.js";
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
const globals = await fetchGlobals();
|
const [globals, books] = await Promise.all([
|
||||||
const books = await fetchBooks();
|
fetchGlobals(),
|
||||||
|
fetchBooks(),
|
||||||
|
]);
|
||||||
const feed = generateJsonFeed({
|
const feed = generateJsonFeed({
|
||||||
permalink: "/feeds/books.json",
|
permalink: "/feeds/books.json",
|
||||||
title: "Books / Cory Dransfeldt",
|
title: "Books / Cory Dransfeldt",
|
||||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
||||||
import { fetchBooks } from "@utils/data/books.js";
|
import { fetchBooks } from "@utils/data/books.js";
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
const globals = await fetchGlobals();
|
const [globals, books] = await Promise.all([
|
||||||
const books = await fetchBooks();
|
fetchGlobals(),
|
||||||
|
fetchBooks(),
|
||||||
|
]);
|
||||||
const rss = generateRssFeed({
|
const rss = generateRssFeed({
|
||||||
permalink: "/feeds/books.xml",
|
permalink: "/feeds/books.xml",
|
||||||
title: "Books feed",
|
title: "Books feed",
|
||||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
||||||
import { fetchLinks } from "@utils/data/links.js";
|
import { fetchLinks } from "@utils/data/links.js";
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
const globals = await fetchGlobals();
|
const [globals, links] = await Promise.all([
|
||||||
const links = await fetchLinks();
|
fetchGlobals(),
|
||||||
|
fetchLinks(),
|
||||||
|
]);
|
||||||
const feed = generateJsonFeed({
|
const feed = generateJsonFeed({
|
||||||
permalink: "/feeds/links.json",
|
permalink: "/feeds/links.json",
|
||||||
title: "Links / Cory Dransfeldt",
|
title: "Links / Cory Dransfeldt",
|
||||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
||||||
import { fetchLinks } from "@utils/data/links";
|
import { fetchLinks } from "@utils/data/links";
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
const globals = await fetchGlobals();
|
const [globals, links] = await Promise.all([
|
||||||
const links = await fetchLinks();
|
fetchGlobals(),
|
||||||
|
fetchLinks(),
|
||||||
|
]);
|
||||||
const rss = generateRssFeed({
|
const rss = generateRssFeed({
|
||||||
permalink: "/feeds/links.xml",
|
permalink: "/feeds/links.xml",
|
||||||
title: "Links feed",
|
title: "Links feed",
|
||||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from '@utils/data/globals.js';
|
||||||
import { fetchMovies } from '@utils/data/movies';
|
import { fetchMovies } from '@utils/data/movies';
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
const globals = await fetchGlobals();
|
const [globals, movies] = await Promise.all([
|
||||||
const movies = await fetchMovies();
|
fetchGlobals(),
|
||||||
|
fetchMovies(),
|
||||||
|
]);
|
||||||
const feed = generateJsonFeed({
|
const feed = generateJsonFeed({
|
||||||
permalink: "/feeds/movies.json",
|
permalink: "/feeds/movies.json",
|
||||||
title: "Movies / Cory Dransfeldt",
|
title: "Movies / Cory Dransfeldt",
|
||||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
||||||
import { fetchMovies } from "@utils/data/movies.js";
|
import { fetchMovies } from "@utils/data/movies.js";
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
const globals = await fetchGlobals();
|
const [globals, movies] = await Promise.all([
|
||||||
const movies = await fetchMovies();
|
fetchGlobals(),
|
||||||
|
fetchMovies(),
|
||||||
|
]);
|
||||||
const rss = generateRssFeed({
|
const rss = generateRssFeed({
|
||||||
permalink: "/feeds/movies.xml",
|
permalink: "/feeds/movies.xml",
|
||||||
title: "Movies feed",
|
title: "Movies feed",
|
||||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
||||||
import { fetchAllPosts } from "@utils/data/posts.js";
|
import { fetchAllPosts } from "@utils/data/posts.js";
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
const globals = await fetchGlobals();
|
const [globals, posts] = await Promise.all([
|
||||||
const posts = await fetchAllPosts();
|
fetchGlobals(),
|
||||||
|
fetchAllPosts(),
|
||||||
|
]);
|
||||||
const feed = generateJsonFeed({
|
const feed = generateJsonFeed({
|
||||||
permalink: "/feeds/posts.json",
|
permalink: "/feeds/posts.json",
|
||||||
title: "Posts / Cory Dransfeldt",
|
title: "Posts / Cory Dransfeldt",
|
||||||
|
|
|
@ -3,9 +3,10 @@ import { fetchGlobals } from "@utils/data/globals.js";
|
||||||
import { fetchAllPosts } from "@utils/data/posts.js";
|
import { fetchAllPosts } from "@utils/data/posts.js";
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
const globals = await fetchGlobals();
|
const [globals, posts] = await Promise.all([
|
||||||
const posts = await fetchAllPosts();
|
fetchGlobals(),
|
||||||
|
fetchAllPosts(),
|
||||||
|
]);
|
||||||
const rss = generateRssFeed({
|
const rss = generateRssFeed({
|
||||||
permalink: "/feeds/posts.xml",
|
permalink: "/feeds/posts.xml",
|
||||||
title: "Posts feed",
|
title: "Posts feed",
|
||||||
|
|
|
@ -45,9 +45,10 @@ const generateSyndicationRSS = async (globals, entries) => {
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
try {
|
try {
|
||||||
const globals = await fetchGlobals();
|
const [globals, entries] = await Promise.all([
|
||||||
const entries = await fetchSyndication();
|
fetchGlobals(),
|
||||||
|
fetchSyndication(),
|
||||||
|
]);
|
||||||
const rss = await generateSyndicationRSS(globals, entries);
|
const rss = await generateSyndicationRSS(globals, entries);
|
||||||
|
|
||||||
return new Response(rss, {
|
return new Response(rss, {
|
||||||
|
|
|
@ -1,48 +1,53 @@
|
||||||
---
|
---
|
||||||
import Layout from "@layouts/Layout.astro";
|
import Layout from "@layouts/Layout.astro";
|
||||||
import Paginator from "@components/nav/Paginator.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";
|
import { fetchLinks } from "@utils/data/links.js";
|
||||||
|
|
||||||
export const prerender = true;
|
export const prerender = true;
|
||||||
|
|
||||||
const links = await fetchLinks();
|
export const getStaticPaths = async ({ paginate }) => {
|
||||||
const title = "Links";
|
const links = await fetchLinks();
|
||||||
const description =
|
return paginate(links, {
|
||||||
"These are links I've liked or otherwise found interesting. They're all added manually, after having been read and, I suppose, properly considered.";
|
pageSize: 30,
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
||||||
|
const { page } = Astro.props;
|
||||||
|
|
||||||
|
const paginatedLinks = page.data;
|
||||||
const pagination = {
|
const pagination = {
|
||||||
currentPage,
|
currentPage: page.currentPage,
|
||||||
totalPages,
|
totalPages: page.lastPage,
|
||||||
hasPrevious: currentPage > 1,
|
hasPrevious: page.currentPage > 1,
|
||||||
hasNext: currentPage < totalPages,
|
hasNext: page.currentPage < page.lastPage,
|
||||||
previousPage: currentPage > 1 ? `/links?page=${currentPage - 1}` : null,
|
previousPage: page.url.prev || null,
|
||||||
nextPage: currentPage < totalPages ? `/links?page=${currentPage + 1}` : null,
|
nextPage: page.url.next || null,
|
||||||
pages: Array.from({ length: totalPages }, (_, index) => ({
|
pages: Array.from({ length: page.lastPage }, (_, i) => ({
|
||||||
number: index + 1,
|
number: i + 1,
|
||||||
href: `/links?page=${index + 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
|
<Layout
|
||||||
pageTitle={title}
|
pageTitle={pageTitle}
|
||||||
description={description}
|
description={description}
|
||||||
currentUrl={Astro.url.pathname}
|
currentUrl={Astro.url.pathname}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
currentPage === 1 && (
|
pagination.currentPage === 1 && (
|
||||||
<>
|
<>
|
||||||
<h2 class="page-title">{title}</h2>
|
<h2 class="page-title">{pageTitle}</h2>
|
||||||
<p>{description}</p>
|
<p>{description}</p>
|
||||||
<RssBanner
|
<Rss
|
||||||
url="/feeds/links"
|
url="/feeds/links"
|
||||||
text="Subscribe to my links feed or follow along on this page"
|
text="Subscribe to my links feed or follow along on this page"
|
||||||
/>
|
/>
|
||||||
|
@ -50,7 +55,6 @@ const pagination = {
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="link-grid">
|
<div class="link-grid">
|
||||||
{
|
{
|
||||||
paginatedLinks.map((link) => (
|
paginatedLinks.map((link) => (
|
||||||
|
@ -68,6 +72,5 @@ const pagination = {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Paginator pagination={pagination} />
|
<Paginator pagination={pagination} />
|
||||||
</Layout>
|
</Layout>
|
|
@ -19,8 +19,10 @@ import { mediaLinks } from "@utils/helpers/media.js";
|
||||||
export const prerender = true;
|
export const prerender = true;
|
||||||
|
|
||||||
const { globals } = await fetchGlobalData(Astro);
|
const { globals } = await fetchGlobalData(Astro);
|
||||||
const music = await fetchMusicData();
|
const [music, albumReleases ] = await Promise.all([
|
||||||
const albumReleases = await fetchAlbumReleases();
|
fetchMusicData(),
|
||||||
|
fetchAlbumReleases(),
|
||||||
|
]);
|
||||||
|
|
||||||
const title = "Music";
|
const title = "Music";
|
||||||
const description =
|
const description =
|
||||||
|
|
|
@ -5,46 +5,45 @@ import Layout from "@layouts/Layout.astro";
|
||||||
import Paginator from "@components/nav/Paginator.astro";
|
import Paginator from "@components/nav/Paginator.astro";
|
||||||
import { md } from "@utils/helpers/general.js";
|
import { md } from "@utils/helpers/general.js";
|
||||||
import { DateTime } from "luxon";
|
import { DateTime } from "luxon";
|
||||||
|
import type { GetStaticPaths, Page } from "astro";
|
||||||
|
|
||||||
export const prerender = true;
|
export const prerender = true;
|
||||||
|
export const getStaticPaths = async ({ paginate }) => {
|
||||||
export async function getStaticPaths() {
|
|
||||||
const pageSize = 15; // Declare inside this function
|
|
||||||
const posts = await fetchAllPosts();
|
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);
|
return paginate(sortedPosts, {
|
||||||
const paths = Array.from({ length: totalPages }, (_, i) => ({
|
pageSize: 15,
|
||||||
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);
|
|
||||||
|
|
||||||
|
const { page } = Astro.props;
|
||||||
|
const paginatedPosts = page.data;
|
||||||
const pagination = {
|
const pagination = {
|
||||||
currentPage,
|
currentPage: page.currentPage,
|
||||||
totalPages,
|
totalPages: page.lastPage,
|
||||||
hasPrevious: currentPage > 1,
|
hasPrevious: page.currentPage > 1,
|
||||||
hasNext: currentPage < totalPages,
|
hasNext: page.currentPage < page.lastPage,
|
||||||
previousPage: currentPage > 1 ? `/posts/${currentPage - 1}` : null,
|
previousPage: page.url.prev || null,
|
||||||
nextPage: currentPage < totalPages ? `/posts/${currentPage + 1}` : null,
|
nextPage: page.url.next || null,
|
||||||
pages: Array.from({ length: totalPages }, (_, i) => ({
|
pages: Array.from({ length: page.lastPage }, (_, i) => ({
|
||||||
number: i + 1,
|
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) => (
|
paginatedPosts.map((post) => (
|
||||||
<article>
|
<article>
|
||||||
|
@ -61,6 +60,5 @@ const pagination = {
|
||||||
</article>
|
</article>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
<Paginator pagination={pagination} />
|
<Paginator pagination={pagination} />
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
|
@ -7,9 +7,11 @@ import { fetchGlobalData } from "@utils/data/global/index.js";
|
||||||
import { md } from "@utils/helpers/general.js";
|
import { md } from "@utils/helpers/general.js";
|
||||||
import { getPopularPosts } from "@utils/getPopularPosts.js";
|
import { getPopularPosts } from "@utils/getPopularPosts.js";
|
||||||
|
|
||||||
const analytics = await fetchAnalyticsData();
|
const [analytics, links, posts] = await Promise.all([
|
||||||
const links = await fetchLinks();
|
fetchAnalyticsData(),
|
||||||
const posts = await fetchAllPosts();
|
fetchLinks(),
|
||||||
|
fetchAllPosts(),
|
||||||
|
]);
|
||||||
const popularPosts = getPopularPosts(posts, analytics);
|
const popularPosts = getPopularPosts(posts, analytics);
|
||||||
|
|
||||||
import AddonLinks from "@components/blocks/links/AddonLinks.astro";
|
import AddonLinks from "@components/blocks/links/AddonLinks.astro";
|
||||||
|
@ -91,10 +93,7 @@ const htmlContent = md(post.content);
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
<div set:html={htmlContent} />
|
<div set:html={htmlContent} />
|
||||||
{
|
{post.blocks && <BlockRenderer blocks={post.blocks} />}
|
||||||
post.blocks &&
|
|
||||||
post.blocks.map((block) => <BlockRenderer block={block} />)
|
|
||||||
}
|
|
||||||
{post.mastodon_url && <Mastodon url={post.mastodon_url} />}
|
{post.mastodon_url && <Mastodon url={post.mastodon_url} />}
|
||||||
<AssociatedMedia
|
<AssociatedMedia
|
||||||
artists={post.artists}
|
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
|
// urls
|
||||||
export const encodeAmp = (url) => url.replace(/&/g, "&");
|
export const encodeAmp = (url) => url.replace(/&/g, "&");
|
||||||
export const removeTrailingSlash = (url) => url.replace(/\/$/, '');
|
export const removeTrailingSlash = (url) => url.replace(/\/$/, "");
|
||||||
|
|
||||||
// dates
|
// dates
|
||||||
export const dateToRFC822 = (date) =>
|
export const dateToRFC822 = (date) =>
|
||||||
|
|
Reference in a new issue