From 837433364d3db45e23cd3a3a291ddb0be409c91b Mon Sep 17 00:00:00 2001 From: Bad Manners <me@badmanners.xyz> Date: Fri, 12 Apr 2024 18:15:03 -0300 Subject: [PATCH] Add platforms to GameLayout --- examples/game.md | 1 + examples/user.yaml | 2 +- package-lock.json | 4 +-- package.json | 2 +- src/components/WidthSlotSwitcher.astro | 36 +++++++++++++++++++ src/content/config.ts | 22 +++++------- src/content/games/crossing-over.md | 6 ++++ src/i18n/index.ts | 29 +++++++++++++++ src/layouts/GalleryLayout.astro | 5 ++- src/layouts/GameLayout.astro | 27 +++++++++----- src/layouts/StoryLayout.astro | 20 +++++++---- src/pages/feed.xml.ts | 8 +++-- src/pages/search.astro | 5 ++- .../stories/the-lost-of-the-marshes.astro | 14 ++++++-- src/pages/tags.astro | 2 +- src/styles/base.css | 3 +- 16 files changed, 144 insertions(+), 42 deletions(-) create mode 100644 src/components/WidthSlotSwitcher.astro diff --git a/examples/game.md b/examples/game.md index bdc1e52..b07c1ad 100644 --- a/examples/game.md +++ b/examples/game.md @@ -12,6 +12,7 @@ description: | Some funny text. # descriptionPlaintext: > # Some funny text. +platforms: [web, windows, linux, macos, android, ios] # mastodonPost: # instance: meow.social # user: BadManners diff --git a/examples/user.yaml b/examples/user.yaml index 044d44d..b180ede 100644 --- a/examples/user.yaml +++ b/examples/user.yaml @@ -9,7 +9,7 @@ links: furaffinity: - https://www.furaffinity.net/user/NamelessUser - Nameless_User - inkbunny: https://inkbunny.net/NamelessManners + inkbunny: https://inkbunny.net/NamelessUser sofurry: - https://nameless-user.sofurry.com/ - Nameless User diff --git a/package-lock.json b/package-lock.json index 9ba452b..12df10b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gallery-badmanners-xyz", - "version": "1.3.0", + "version": "1.3.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "gallery-badmanners-xyz", - "version": "1.3.0", + "version": "1.3.1", "dependencies": { "@astrojs/check": "^0.5.10", "@astrojs/rss": "^4.0.5", diff --git a/package.json b/package.json index 9744152..4848f28 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "gallery-badmanners-xyz", "type": "module", - "version": "1.3.0", + "version": "1.3.1", "scripts": { "dev": "astro dev", "start": "astro dev", diff --git a/src/components/WidthSlotSwitcher.astro b/src/components/WidthSlotSwitcher.astro new file mode 100644 index 0000000..eb7c03c --- /dev/null +++ b/src/components/WidthSlotSwitcher.astro @@ -0,0 +1,36 @@ +--- +type Props = { + breakpoint: number; +}; + +const { breakpoint } = Astro.props; +--- + +<div id="slot-switch" data-breakpoint={breakpoint}></div> +<template id="slot-switch-big"> + <slot /> +</template> +<template id="slot-switch-small"> + <slot /> +</template> + +<script> + const slotSwitch = document.querySelector("div#slot-switch") as HTMLDivElement; + const breakpointAttribute = slotSwitch.getAttribute("data-breakpoint"); + if (breakpointAttribute && !isNaN(parseInt(breakpointAttribute))) { + const breakpoint = parseInt(breakpointAttribute); + const slotSwitchBig = document.querySelector("template#slot-switch-big") as HTMLTemplateElement; + const slotSwitchSmall = document.querySelector("template#slot-switch-small") as HTMLTemplateElement; + let displayBig: boolean | null = null; + function handleResize() { + const newDisplayBig = window.innerWidth >= breakpoint; + if (newDisplayBig !== displayBig) { + displayBig = newDisplayBig; + const template = displayBig ? slotSwitchBig : slotSwitchSmall; + slotSwitch.replaceChildren(template.content.cloneNode(true)); + } + } + window.addEventListener("resize", handleResize); + handleResize(); + } +</script> diff --git a/src/content/config.ts b/src/content/config.ts index 7f931ae..c0e2c49 100644 --- a/src/content/config.ts +++ b/src/content/config.ts @@ -22,6 +22,7 @@ const refineCopyrightedCharacters = (value: Record<string, any>) => !("" in valu const lang = z.enum(["eng", "tok"]).default("eng"); const website = z.enum(WEBSITE_LIST); +const platform = z.enum(["web", "windows", "linux", "macos", "android", "ios"]); const mastodonPost = z.object({ instance: z.string(), user: z.string(), @@ -48,7 +49,7 @@ const storiesCollection = defineCollection({ authors: z .union([reference("users"), z.array(reference("users"))]) .default("bad-manners") - .refine(refineAuthors, "authors cannot be empty"), + .refine(refineAuthors, `"authors" cannot be empty`), descriptionPlaintext: z.string().optional(), summary: z.string().optional(), thumbnail: image().optional(), @@ -60,10 +61,7 @@ const storiesCollection = defineCollection({ copyrightedCharacters: z .record(z.string(), reference("users")) .default({}) - .refine( - refineCopyrightedCharacters, - "copyrightedCharacters cannot have an empty catch-all key with other keys", - ), + .refine(refineCopyrightedCharacters, `"copyrightedCharacters" cannot mix empty catch-all key with other keys`), lang, prev: reference("stories").nullish(), next: reference("stories").nullish(), @@ -88,19 +86,17 @@ const gamesCollection = defineCollection({ authors: z .union([reference("users"), z.array(reference("users"))]) .default("bad-manners") - .refine(refineAuthors, "authors cannot be empty"), + .refine(refineAuthors, `"authors" cannot be empty`), descriptionPlaintext: z.string().optional(), thumbnail: image().optional(), thumbnailWidth: z.number().int().optional(), thumbnailHeight: z.number().int().optional(), series: reference("series").optional(), + platforms: z.array(platform).refine((platforms) => platforms.length > 0, `"platforms" cannot be empty`), copyrightedCharacters: z .record(z.string(), reference("users")) .default({}) - .refine( - refineCopyrightedCharacters, - "copyrightedCharacters cannot have an empty catch-all key with other keys", - ), + .refine(refineCopyrightedCharacters, `"copyrightedCharacters" cannot mix empty catch-all key with other keys`), lang, relatedStories: z.array(reference("stories")).default([]), relatedGames: z.array(reference("games")).default([]), @@ -125,7 +121,7 @@ const usersCollection = defineCollection({ .refine( ({ links, preferredLink }) => !preferredLink || preferredLink in links, ({ preferredLink }) => ({ - message: `"${preferredLink}" not defined in links`, + message: `"${preferredLink}" not defined in "links"`, path: ["preferredLink"], }), ), @@ -136,7 +132,7 @@ const seriesCollection = defineCollection({ schema: z.object({ // Required name: z.string(), - url: z.string().regex(localUrlRegex, "must be a local URL"), + url: z.string().regex(localUrlRegex, `"url" must be a local URL`), }), }); @@ -149,7 +145,7 @@ const tagCategoriesCollection = defineCollection({ tags: z.array( z.union([ z.string(), - z.record(lang, z.string()).refine((tag) => "eng" in tag, 'Object-formatted tag must have an "eng" key'), + z.record(lang, z.string()).refine((tag) => "eng" in tag, `object-formatted tag must have an "eng" key`), ]), ), }), diff --git a/src/content/games/crossing-over.md b/src/content/games/crossing-over.md index 2d169b3..348a579 100644 --- a/src/content/games/crossing-over.md +++ b/src/content/games/crossing-over.md @@ -26,6 +26,12 @@ descriptionPlaintext: > An original soundtrack with 9 exclusive songs; A challenging physics-based fishing minigame with scaling difficulty; And a special cutscene... +platforms: + - web + - windows + - linux + - macos + - android mastodonPost: instance: meow.social user: BadManners diff --git a/src/i18n/index.ts b/src/i18n/index.ts index bb029f9..f11fa95 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -91,6 +91,9 @@ export const UI_STRINGS: Record<string, TranslationRecord> = { "story/requested_by": { eng: (arg: string) => `Requested by ${arg}`, }, + "story/draft_warning": { + eng: "DRAFT VERSION – DO NOT REDISTRIBUTE", + }, "characters/characters_are_copyrighted_by": { eng: (owner: string, charactersList: string[]) => charactersList.length == 1 @@ -100,6 +103,32 @@ export const UI_STRINGS: Record<string, TranslationRecord> = { "characters/all_characters_are_copyrighted_by": { eng: (owner: string) => `All characters are © ${owner}`, }, + "game/platforms": { + eng: (platforms: string[]) => { + const translatedPlatforms = platforms.map( + (platform) => (UI_STRINGS[`game/platform_${platform}`]?.eng as string | undefined) || platform, + ); + return `A game for ${(UI_STRINGS["util/join_names"]!.eng as (arg: string[]) => string)(translatedPlatforms)}`; + }, + }, + "game/platform_web": { + eng: "web browsers", + }, + "game/platform_windows": { + eng: "Windows", + }, + "game/platform_linux": { + eng: "Linux", + }, + "game/platform_macos": { + eng: "macOS", + }, + "game/platform_android": { + eng: "Android", + }, + "game/platform_ios": { + eng: "iOS", + }, }; export function t(lang: Lang, stringOrSource: string | TranslationRecord, ...args: any[]): string { diff --git a/src/layouts/GalleryLayout.astro b/src/layouts/GalleryLayout.astro index 7583b74..0cb23b6 100644 --- a/src/layouts/GalleryLayout.astro +++ b/src/layouts/GalleryLayout.astro @@ -70,7 +70,10 @@ const logo = await getImage({ src: logoBM, width: 192 }); </button> </div> </div> - <main class="ml-0 max-w-6xl px-2 pb-12 pt-4 md:ml-60 md:px-4 print:pb-0" data-pagefind-body={enablePagefind ? "" : undefined}> + <main + class="ml-0 max-w-6xl px-2 pb-12 pt-4 md:ml-60 md:px-4 print:pb-0" + data-pagefind-body={enablePagefind ? "" : undefined} + > <slot /> </main> </div> diff --git a/src/layouts/GameLayout.astro b/src/layouts/GameLayout.astro index 3db7952..96c443b 100644 --- a/src/layouts/GameLayout.astro +++ b/src/layouts/GameLayout.astro @@ -61,10 +61,18 @@ const thumbnail = <meta property="og:title" content={props.title} data-pagefind-meta="title[content]" /> <meta property="og:description" content={props.contentWarning} /> <meta property="og:url" content={Astro.url} data-pagefind-meta="url[content]" /> - {thumbnail ? <Fragment> - <meta content={thumbnail.src} property="og:image" data-pagefind-meta="image[content]" /> - <meta property="og:image:alt" content={`Cover art for ${props.title}`} data-pagefind-meta="image_alt[content]" /> - </Fragment> : null} + { + thumbnail ? ( + <Fragment> + <meta content={thumbnail.src} property="og:image" data-pagefind-meta="image[content]" /> + <meta + property="og:image:alt" + content={`Cover art for ${props.title}`} + data-pagefind-meta="image_alt[content]" + /> + </Fragment> + ) : null + } <meta name="theme-color" content="#7DD05A" data-react-helmet="true" /> </Fragment> <div @@ -134,10 +142,13 @@ const thumbnail = <Authors lang={props.lang}> {authors.map((author) => <UserComponent lang={props.lang} user={author} />)} </Authors> + <div id="platforms"> + <p>{t(props.lang, "game/platforms", props.platforms)}</p> + </div> { props.isDraft ? ( <p id="draft-warning" class="py-2 text-center text-2xl font-semibold not-italic text-red-600"> - DRAFT VERSION – DO NOT REDISTRIBUTE + {t(props.lang, "story/draft_warning")} </p> ) : null } @@ -174,7 +185,7 @@ const thumbnail = id="draft-warning-bottom" class="py-2 text-center font-serif text-2xl font-semibold not-italic text-red-600" > - DRAFT VERSION – DO NOT REDISTRIBUTE + {t(props.lang, "story/draft_warning")} </p> ) : ( <p @@ -195,7 +206,7 @@ const thumbnail = } <section id="description" class="px-2 font-serif" aria-describedby="title-description"> <h2 id="title-description" class="py-2 font-serif text-xl font-semibold text-stone-800 dark:text-stone-100"> - Description + {t(props.lang, "story/description")} </h2> <Prose> <Markdown of={props.description} /> @@ -208,7 +219,7 @@ const thumbnail = ><path d="M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z" ></path></svg - ><span>To top</span></a + ><span>{t(props.lang, "story/to_top")}</span></a > </div> <section id="tags" aria-describedby="title-tags" class="my-5"> diff --git a/src/layouts/StoryLayout.astro b/src/layouts/StoryLayout.astro index 195c404..a86cbe1 100644 --- a/src/layouts/StoryLayout.astro +++ b/src/layouts/StoryLayout.astro @@ -71,10 +71,18 @@ const thumbnail = <meta property="og:title" content={props.title} data-pagefind-meta="title[content]" /> <meta property="og:description" content={`Word count: ${props.wordCount}. ${props.contentWarning}`} /> <meta property="og:url" content={Astro.url} data-pagefind-meta="url[content]" /> - {thumbnail ? <Fragment> - <meta content={thumbnail.src} property="og:image" data-pagefind-meta="image[content]" /> - <meta property="og:image:alt" content={`Cover art for ${props.shortTitle || props.title}`} data-pagefind-meta="image_alt[content]" /> - </Fragment> : null} + { + thumbnail ? ( + <Fragment> + <meta content={thumbnail.src} property="og:image" data-pagefind-meta="image[content]" /> + <meta + property="og:image:alt" + content={`Cover art for ${props.shortTitle || props.title}`} + data-pagefind-meta="image_alt[content]" + /> + </Fragment> + ) : null + } <meta name="theme-color" content="#7DD05A" data-react-helmet="true" /> </Fragment> <div @@ -184,7 +192,7 @@ const thumbnail = { props.isDraft ? ( <p id="draft-warning" class="py-2 text-center text-2xl font-semibold not-italic text-red-600"> - DRAFT VERSION – DO NOT REDISTRIBUTE + {t(props.lang, "story/draft_warning")} </p> ) : null } @@ -236,7 +244,7 @@ const thumbnail = id="draft-warning-bottom" class="py-2 text-center font-serif text-2xl font-semibold not-italic text-red-600" > - DRAFT VERSION – DO NOT REDISTRIBUTE + {t(props.lang, "story/draft_warning")} </p> ) : ( <p diff --git a/src/pages/feed.xml.ts b/src/pages/feed.xml.ts index 9539c75..e4e91f9 100644 --- a/src/pages/feed.xml.ts +++ b/src/pages/feed.xml.ts @@ -83,9 +83,10 @@ export const GET: APIRoute = async ({ site }) => { title: `New game! "${data.title}"`, pubDate: toNoonUTCDate(data.pubDate), link: `/games/${slug}`, - description: `${data.contentWarning} ${data.descriptionPlaintext || data.description}` - .replaceAll(/[\n ]+/g, " ") - .trim(), + description: + `${t(data.lang, "game/platforms", data.platforms)}. ${data.contentWarning} ${data.descriptionPlaintext || data.description}` + .replaceAll(/[\n ]+/g, " ") + .trim(), categories: ["game"], content: sanitizeHtml( `<h1>${data.title}</h1>` + @@ -101,6 +102,7 @@ export const GET: APIRoute = async ({ site }) => { ); }), )}</p>` + + `<hr><p>${t(data.lang, "game/platforms", data.platforms)}</p>` + `<hr><p><em>${data.contentWarning.trim()}</em></p>` + `<hr>${tinyDecode(await marked(body))}` + `<hr>${tinyDecode(await marked(data.description))}`, diff --git a/src/pages/search.astro b/src/pages/search.astro index 9ca1bf5..8556985 100644 --- a/src/pages/search.astro +++ b/src/pages/search.astro @@ -1,11 +1,10 @@ --- import SearchComponent from "astro-pagefind/components/Search"; import GalleryLayout from "../layouts/GalleryLayout.astro"; - --- <GalleryLayout pageTitle="Search"> <meta slot="head-description" property="og:description" content="Bad Manners || Search" /> <h1 class="m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">Search</h1> - <SearchComponent id="search" className="my-4 pagefind-ui" /> -</GalleryLayout> \ No newline at end of file + <SearchComponent id="search" className="pagefind-ui my-4" /> +</GalleryLayout> diff --git a/src/pages/stories/the-lost-of-the-marshes.astro b/src/pages/stories/the-lost-of-the-marshes.astro index 69d33e4..b57997c 100644 --- a/src/pages/stories/the-lost-of-the-marshes.astro +++ b/src/pages/stories/the-lost-of-the-marshes.astro @@ -16,11 +16,21 @@ const mainChaptersWithSummaries = mainChapters.filter((story) => story.data.summ --- <GalleryLayout pageTitle={series.data.name} enablePagefind={true}> - <meta slot="head-description" property="og:description" content="Bad Manners || The story of Quince, Nikili, and Suu."/> + <meta + slot="head-description" + property="og:description" + content="Bad Manners || The story of Quince, Nikili, and Suu." + /> <h1 class="m-2 text-2xl font-semibold text-stone-800 dark:text-stone-100">{series.data.name}</h1> <p class="my-4">This is the main hub for the story of Quince, Nikili, and Suu, as well as all bonus content.</p> <section class="my-2" aria-labelledby="main-chapters"> - <h2 id="main-chapters" class="p-2 text-xl font-semibold text-stone-800 dark:text-stone-100" data-pagefind-meta="type:series">Main chapters</h2> + <h2 + id="main-chapters" + class="p-2 text-xl font-semibold text-stone-800 dark:text-stone-100" + data-pagefind-meta="type:series" + > + Main chapters + </h2> <details class="mx-3 mb-6 mt-1 rounded-lg border border-stone-400 bg-stone-300 dark:border-stone-500 dark:bg-stone-700" > diff --git a/src/pages/tags.astro b/src/pages/tags.astro index 4864988..81f3611 100644 --- a/src/pages/tags.astro +++ b/src/pages/tags.astro @@ -84,7 +84,7 @@ if (uncategorizedTagsSet.size > 0) { <GalleryLayout pageTitle="Tags"> <meta - property="og:description" + property="og:description" slot="head-description" content="Bad Manners || Find all content with a specific tag." /> diff --git a/src/styles/base.css b/src/styles/base.css index b21b740..da6dd59 100644 --- a/src/styles/base.css +++ b/src/styles/base.css @@ -3,7 +3,8 @@ @tailwind utilities; @layer components { - .text-link, .pagefind-ui .pagefind-ui__result-link { + .text-link, + .pagefind-ui .pagefind-ui__result-link { @apply text-stone-800 hover:text-bm-500 focus:text-bm-500 dark:text-zinc-300 dark:hover:text-bm-400 dark:focus:text-bm-400; }