chore: myriad fixes + genre pages

This commit is contained in:
Cory Dransfeldt 2024-11-18 09:57:12 -08:00
parent 67ab04b241
commit 0d8408c680
No known key found for this signature in database
28 changed files with 1963 additions and 238 deletions

View file

@ -14,6 +14,9 @@ export default defineConfig({
vite: {
build: {
sourcemap: false,
rollupOptions: {
external: ["/js/script.js"],
},
},
optimizeDeps: {
include: ["@tabler/icons-react"],

1922
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -12,20 +12,23 @@
"deploy:worker": "wrangler deploy --env production --config workers/$npm_config_worker/wrangler.toml"
},
"dependencies": {
"@astrojs/check": "0.9.4",
"@astrojs/cloudflare": "^11.2.0",
"@astrojs/react": "^3.6.2",
"@supabase/supabase-js": "^2.46.1",
"@tabler/icons-react": "^3.22.0",
"astro": "^4.16.13",
"luxon": "^3.5.0",
"markdown-it": "^14.1.0",
"markdown-it-anchor": "^9.2.0",
"markdown-it-footnote": "^4.0.0",
"markdown-it-prism": "^2.3.0",
"rehype-prism-plus": "2.0.0",
"rehype-stringify": "10.0.1",
"remark": "15.0.1",
"remark-footnotes": "4.0.1",
"remark-rehype": "11.1.1",
"truncate-html": "1.1.2",
"typescript": "5.6.3",
"youtube-video-element": "^1.1.6"
},
"devDependencies": {
"@supabase/supabase-js": "^2.46.1",
"ics": "^3.8.1"
}
}

View file

@ -53,7 +53,7 @@ function getImageAttributes(item, shape) {
<div class="subheader">{item.grid.subtext}</div>
</div>
<img
srcSet={`
srcset={`
${globals.cdn_url}${imageUrl}?class=${imageClass}sm&type=webp ${width}w,
${globals.cdn_url}${imageUrl}?class=${imageClass}md&type=webp ${width * 2}w
`}

View file

@ -12,7 +12,7 @@ const { globals } = await fetchGlobalData(Astro);
<div class="meta">
<a href={item.chart.url}>
<img
srcSet={`
srcset={`
${globals.cdn_url}${item.chart.image}?class=w50&type=webp 50w,
${globals.cdn_url}${item.chart.image}?class=w100&type=webp 100w
`}
@ -32,7 +32,7 @@ const { globals } = await fetchGlobalData(Astro);
<span class="subtext">{item.chart.subtext}</span>
</div>
</div>
<time dateTime={item.chart.played_at}>
<time datetime={item.chart.played_at}>
{new Date(item.chart.played_at).toLocaleString("en-US", {
timeZone: "America/Los_Angeles",
month: "long",

View file

@ -1,5 +1,4 @@
---
import { fetchGlobalData } from "@utils/data/global/index.js";
import Hero from "@components/blocks/Hero.astro";
const { movie } = Astro.props;

View file

@ -2,7 +2,6 @@
import Layout from "@layouts/Layout.astro";
import BlockRenderer from "@components/blocks/BlockRenderer.astro";
import { fetchPages } from "@utils/data/pages.js";
import { fetchGlobalData } from "@utils/data/global/index.js";
export const prerender = true;
@ -15,7 +14,6 @@ export async function getStaticPaths() {
}
const { page } = Astro.props;
const { globals } = await fetchGlobalData(Astro);
const currentUrl = Astro.url.pathname;
---

View file

@ -53,7 +53,7 @@ const bookYearLinks = (years) =>
<article class="book-entry" key={book.url}>
<a href={book.url}>
<img
srcSet={`
srcset={`
${globals.cdn_url}${book.image}?class=verticalsm&type=webp 200w,
${globals.cdn_url}${book.image}?class=verticalmd&type=webp 400w
`}

View file

@ -7,7 +7,6 @@ import {
findFavoriteBooks,
mediaLinks,
} from "@utils/helpers/media.js";
import { fetchGlobalData } from "@utils/data/global/index.js";
import { fetchBooks } from "@utils/data/books.js";
import { DateTime } from "luxon";
@ -21,7 +20,6 @@ export async function getStaticPaths() {
}));
}
const { globals } = await fetchGlobalData(Astro);
const books = await fetchBooks();
const { year } = Astro.params;
const yearData = books.years.find((y) => y.value === parseInt(year, 10));

View file

@ -2,8 +2,6 @@ import { generateJsonFeed } from "@utils/generateJsonFeed.js";
import { fetchGlobals } from "@utils/data/globals.js";
import { fetchActivity } from "@utils/data/activity.js";
export const prerender = true;
export async function GET() {
const globals = await fetchGlobals();
const activity = await fetchActivity();
@ -15,11 +13,7 @@ export async function GET() {
data: activity,
});
const filePath = path.resolve("public/feeds/all.json");
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, feed);
return new Response(feed, {
return new Response(feed, {
status: 200,
headers: {
"Content-Type": "application/json",

View file

@ -2,8 +2,6 @@ import { generateRssFeed } from "@utils/generateRssFeed";
import { fetchGlobals } from "@utils/data/globals.js";
import { fetchActivity } from "@utils/data/activity.js";
export const prerender = true;
export async function GET() {
const globals = await fetchGlobals();
const activity = await fetchActivity();
@ -15,10 +13,6 @@ export async function GET() {
data: activity,
});
const filePath = path.resolve("public/feeds/all.xml");
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, rss);
return new Response(rss, {
status: 200,
headers: {

View file

@ -2,8 +2,6 @@ import { generateJsonFeed } from "@utils/generateJsonFeed.js";
import { fetchGlobals } from "@utils/data/globals.js";
import { fetchBooks } from "@utils/data/books.js";
export const prerender = true;
export async function GET() {
const globals = await fetchGlobals();
const books = await fetchBooks();
@ -15,10 +13,6 @@ export async function GET() {
data: books.feed,
});
const filePath = path.resolve("public/feeds/books.json");
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, feed);
return new Response(feed, {
status: 200,
headers: {

View file

@ -2,8 +2,6 @@ import { generateRssFeed } from "@utils/generateRssFeed";
import { fetchGlobals } from "@utils/data/globals.js";
import { fetchBooks } from "@utils/data/books.js";
export const prerender = true;
export async function GET() {
const globals = await fetchGlobals();
const books = await fetchBooks();
@ -15,10 +13,6 @@ export async function GET() {
data: books.feed,
});
const filePath = path.resolve("public/feeds/books.xml");
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, rss);
return new Response(rss, {
status: 200,
headers: {

View file

@ -2,8 +2,6 @@ import { generateJsonFeed } from "@utils/generateJsonFeed.js";
import { fetchGlobals } from "@utils/data/globals.js";
import { fetchLinks } from "@utils/data/links.js";
export const prerender = true;
export async function GET() {
const globals = await fetchGlobals();
const links = await fetchLinks();
@ -15,10 +13,6 @@ export async function GET() {
data: links,
});
const filePath = path.resolve("public/feeds/links.json");
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, feed);
return new Response(feed, {
status: 200,
headers: {

View file

@ -2,8 +2,6 @@ import { generateRssFeed } from "@utils/generateRssFeed";
import { fetchGlobals } from "@utils/data/globals.js";
import { fetchLinks } from "@utils/data/links";
export const prerender = true;
export async function GET() {
const globals = await fetchGlobals();
const links = await fetchLinks();
@ -15,10 +13,6 @@ export async function GET() {
data: links,
});
const filePath = path.resolve("public/feeds/links.xml");
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, rss);
return new Response(rss, {
status: 200,
headers: {

View file

@ -2,8 +2,6 @@ import { generateJsonFeed } from '@utils/generateJsonFeed.js';
import { fetchGlobals } from '@utils/data/globals.js';
import { fetchMovies } from '@utils/data/movies';
export const prerender = true;
export async function GET() {
const globals = await fetchGlobals();
const movies = await fetchMovies();
@ -14,11 +12,6 @@ export async function GET() {
globals,
data: movies.feed,
});
const filePath = path.resolve("public/feeds/movies.json");
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, feed);
return new Response(feed, {
status: 200,
headers: {

View file

@ -2,23 +2,17 @@ import { generateRssFeed } from "@utils/generateRssFeed";
import { fetchGlobals } from "@utils/data/globals.js";
import { fetchMovies } from "@utils/data/movies.js";
export const prerender = true;
export async function getStaticPaths() {
export async function GET() {
const globals = await fetchGlobals();
const movies = await fetchMovies();
const rss = GET({
const rss = generateRssFeed({
permalink: "/feeds/movies.xml",
title: "Movies feed",
globals,
data: movies.feed,
});
const filePath = path.resolve("public/feeds/movies.xml");
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, rss);
return new Response(rss, {
status: 200,
headers: {

View file

@ -2,8 +2,6 @@ import { generateJsonFeed } from "@utils/generateJsonFeed.js";
import { fetchGlobals } from "@utils/data/globals.js";
import { fetchAllPosts } from "@utils/data/posts.js";
export const prerender = true;
export async function GET() {
const globals = await fetchGlobals();
const posts = await fetchAllPosts();
@ -15,10 +13,6 @@ export async function GET() {
data: posts,
});
const filePath = path.resolve("public/feeds/posts.json");
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, feed);
return new Response(feed, {
status: 200,
headers: {

View file

@ -2,23 +2,17 @@ import { generateRssFeed } from "@utils/generateRssFeed";
import { fetchGlobals } from "@utils/data/globals.js";
import { fetchAllPosts } from "@utils/data/posts.js";
export const prerender = true;
export async function getStaticPaths() {
export async function GET() {
const globals = await fetchGlobals();
const posts = await fetchAllPosts();
const rss = GET({
const rss = generateRssFeed({
permalink: "/feeds/posts.xml",
title: "Posts feed",
globals,
data: posts,
});
const filePath = path.resolve("public/feeds/posts.xml");
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, rss);
return new Response(rss, {
status: 200,
headers: {

View file

@ -3,10 +3,7 @@ import fetchSyndication from '@utils/data/syndication.js';
import { fetchGlobals } from '@utils/data/globals.js';
import { dateToRFC822, encodeAmp, md } from '@utils/helpers/general.js';
const generateSyndicationRSS = async () => {
const globals = await fetchGlobals();
const entries = await fetchSyndication();
const generateSyndicationRSS = async (globals, entries) => {
if (!entries.length) throw new Error('No feed entries found.');
const title = globals.site_name || 'Syndicated Content Feed';
@ -48,15 +45,17 @@ const generateSyndicationRSS = async () => {
export async function GET() {
try {
const rss = await generateSyndicationRSS();
const globals = await fetchGlobals();
const entries = await fetchSyndication();
const rss = await generateSyndicationRSS(globals, entries);
return new Response(rss, {
status: 200,
headers: { 'Content-Type': 'application/rss+xml' },
});
} catch (error) {
console.error(error.message);
console.error('Error generating syndication feed:', error);
return new Response('Error generating syndication feed.', { status: 500 });
}
}
export const prerender = true;

View file

@ -3,11 +3,9 @@ import Layout from "@layouts/Layout.astro";
import Paginator from "@components/nav/Paginator.astro";
import RssBanner from "@components/blocks/banners/Rss.astro";
import { fetchLinks } from "@utils/data/links.js";
import { fetchGlobalData } from "@utils/data/global/index.js";
export const prerender = true;
const { globals } = await fetchGlobalData(Astro);
const links = await fetchLinks();
const title = "Links";
const description =

View file

@ -0,0 +1,71 @@
---
import Layout from "@layouts/Layout.astro";
import AssociatedMedia from "@components/blocks/AssociatedMedia.astro";
import { IconArrowLeft } from "@tabler/icons-react";
import { fetchGenreByUrl } from "@utils/data/dynamic/genreByUrl.js";
import { mediaLinks } from "@utils/helpers/media.js";
import { md } from "@utils/helpers/general.js";
const genre = await fetchGenreByUrl(Astro.url.pathname);
if (!genre) return Astro.redirect("/404", 404);
const artistCount = genre.artists?.length || 0;
const connectingWords = artistCount > 1 ? "artists are" : "artist is";
const genreMediaLinks = mediaLinks(genre.artists, "artist", 5);
const pageTitle = `${genre.name} / Music`;
const description = `Discover the music genre ${genre.name}, featuring ${artistCount} artists and ${genre.total_plays} total track plays.`;
---
<Layout
pageTitle={pageTitle}
description={description}
fullUrl={Astro.url.pathname}
schema="genre"
>
<a class="back-link" href="/music" title="Go back to the music index page">
<IconArrowLeft size={18} /> Back to music
</a>
<h2>{genre.name}</h2>
<article class="genre-focus">
{
genreMediaLinks && (
<>
<p>
My top <strong class="highlight-text">{genre.name}</strong>{" "}
{connectingWords} <span set:html={genreMediaLinks}></span> I've listened to{" "}
<strong class="highlight-text">{genre.total_plays}</strong>{" "}
tracks from this genre.
</p>
<hr />
</>
)
}
<AssociatedMedia
books={genre.books}
movies={genre.movies}
posts={genre.posts}
/>
{
genre.description && (
<>
<h3>Overview</h3>
<div data-toggle-content class="text-toggle-hidden">
<div set:html={md(genre.description)} />
<p>
<a href={genre.wiki_link}>Continue reading at Wikipedia.</a>
</p>
<p>
<em>
Wikipedia content provided under the terms of the{" "}
<a href="https://creativecommons.org/licenses/by-sa/3.0/">
Creative Commons BY-SA license
</a>
</em>
</p>
</div>
<button data-toggle-button>Show more</button>
</>
)
}
</article>
</Layout>

View file

@ -1,8 +1,6 @@
---
import { getCollection } from "astro:content";
import { IconStar } from "@tabler/icons-react";
import { fetchAllPosts } from "@data/posts.js";
import { fetchGlobalData } from "@utils/data/global/index.js";
import Layout from "@layouts/Layout.astro";
import Paginator from "@components/nav/Paginator.astro";
import { md } from "@utils/helpers/general.js";
@ -10,13 +8,22 @@ import { DateTime } from "luxon";
export const prerender = true;
const posts = await fetchAllPosts();
const { page } = Astro.props;
const { globals } = await fetchGlobalData(Astro);
const currentUrl = Astro.url.pathname;
export async function getStaticPaths() {
const pageSize = 15; // Declare inside this function
const posts = await fetchAllPosts();
const totalPages = Math.ceil(posts.length / pageSize);
const paths = Array.from({ length: totalPages }, (_, i) => ({
params: { page: (i + 1).toString() },
}));
return paths;
}
const currentPage = Astro.params.page ? parseInt(Astro.params.page, 10) : 1;
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;

View file

@ -318,9 +318,8 @@ article {
}
}
sup.footnote-ref {
line-height: var(--line-height-xs);
padding: var(--spacing-xs);
.footnotes #footnote-label {
display: none;
}
/* tables */

View file

@ -1,4 +1,5 @@
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;
@ -12,7 +13,7 @@ export async function fetchArtistByUrl(url) {
const { data: artist, error } = await supabase
.from("optimized_artists")
.select("*")
.eq("url", url)
.eq("url", removeTrailingSlash(url))
.limit(1);
if (error) {

View file

@ -1,4 +1,5 @@
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;
@ -11,7 +12,7 @@ export async function fetchBookByUrl(url) {
const { data: book, error } = await supabase
.from("optimized_books")
.select("*")
.eq("url", url)
.eq("url", removeTrailingSlash(url))
.limit(1);
if (error || !book || book.length === 0) {

View file

@ -0,0 +1,26 @@
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 genreCache = {};
export async function fetchGenreByUrl(url) {
if (import.meta.env.MODE === "development" && genreCache[url]) return genreCache[url];
const { data: genre, error } = await supabase
.from("optimized_genres")
.select("*")
.eq("url", removeTrailingSlash(url))
.limit(1);
if (error) {
console.error("Error fetching genre:", error);
return null;
}
if (import.meta.env.MODE === "development") genreCache[url] = genre[0];
return genre[0];
}

View file

@ -1,20 +1,11 @@
import { DateTime } from "luxon";
import markdownIt from "markdown-it";
import markdownItAnchor from "markdown-it-anchor";
import markdownItFootnote from "markdown-it-footnote";
import markdownItPrism from "markdown-it-prism";
import { remark } from "remark";
import footnotes from "remark-footnotes";
import remarkRehype from "remark-rehype";
import rehypePrism from "rehype-prism-plus";
import rehypeStringify from "rehype-stringify";
import truncateHtml from "truncate-html";
const markdown = markdownIt({ html: true, linkify: true });
markdown.use(markdownItAnchor, {
level: [1, 2],
permalink: markdownItAnchor.permalink.headerLink({
safariReaderFix: true,
}),
});
markdown.use(markdownItFootnote);
markdown.use(markdownItPrism);
// arrays
export const shuffleArray = (array) => {
const shuffled = [...array];
@ -47,7 +38,16 @@ export const parseCountryField = (countryField) => {
};
// markdown
export const md = (string) => markdown.render(string);
export const md = async (string) => {
const processed = await remark()
.use(footnotes, { inlineNotes: true })
.use(remarkRehype)
.use(rehypePrism, { ignoreMissing: true })
.use(rehypeStringify)
.process(string);
return processed.toString();
};
// html
export const htmlTruncate = (content, limit = 50) =>
@ -66,11 +66,12 @@ export const escapeHtml = (str) =>
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
}[char] || char)
})[char] || char
);
// urls
export const encodeAmp = (url) => url.replace(/&/g, "&amp;");
export const removeTrailingSlash = (url) => url.replace(/\/$/, '');
// dates
export const dateToRFC822 = (date) =>