Improve age-restricted hyperlinks and clean up markup

This commit is contained in:
Bad Manners 2024-08-25 12:36:37 -03:00
parent 67e17ae27f
commit 4f6b320d31
13 changed files with 117 additions and 63 deletions

View file

@ -1,5 +1,5 @@
import { spawn } from "node:child_process";
import { join } from "node:path";
import { join, normalize as n } from "node:path";
import { program, Option } from "commander";
interface DeployLftpOptions {
@ -17,9 +17,9 @@ async function deployLftp({ host, user, password, targetFolder, sourceFolder, as
[
"-c",
[
`open -u ${user},${password} ${host}`,
`mirror --reverse --include-glob ${join(assetsFolder, "*")} --delete --only-missing --no-perms --verbose ${sourceFolder} ${targetFolder}`,
`mirror --reverse --exclude-glob ${join(assetsFolder, "*")} --delete --no-perms --verbose ${sourceFolder} ${targetFolder}`,
`open -u ${user},${password.replaceAll(/([ \t.,:;?!`'"^|*+#&$\(\)\[\]{}<>\\/-])/, "\\$1")} ${host}`,
`mirror --reverse --include-glob ${join(assetsFolder, "*")} --delete --only-missing --no-perms --verbose ${n(sourceFolder)} ${n(targetFolder)}`,
`mirror --reverse --exclude-glob ${join(assetsFolder, "*")} --delete --no-perms --verbose ${n(sourceFolder)} ${n(targetFolder)}`,
`bye`,
].join("\n"),
],
@ -27,14 +27,10 @@ async function deployLftp({ host, user, password, targetFolder, sourceFolder, as
stdio: "inherit",
},
);
await new Promise<void>((resolve, reject) => {
process.on("close", (code) => {
if (code === 0) {
resolve();
} else {
reject(`deploy-lftp failed with code ${code}`);
}
});
await new Promise((resolve, reject) => {
process.on("close", (code) =>
(code === 0) ? resolve(0) : reject(`lftp failed with code ${code}`),
);
});
}

View file

@ -86,6 +86,11 @@ import IconTriangleExclamation from "./icons/IconTriangleExclamation.astro";
localStorage.ageVerified = ageVerified;
document.body.style.overflow = "auto";
document.querySelectorAll("body > :not(#modal-age-restricted)").forEach((el) => el.removeAttribute("inert"));
document.body.querySelectorAll<HTMLElementTagNameMap["a"]>("a[href][data-age-restricted]").forEach((el) => {
let newHref = new URL(el.href);
newHref.searchParams.set("ageVerified", "true");
el.href = newHref.href;
});
modal.style.display = "none";
},
{ once: true },

View file

@ -1,4 +1,4 @@
---
---
<script is:inline>function a(){let b=document,c="#modal-age-restricted",d="true",e=b.querySelector("body > "+c);e&&localStorage.ageVerified!==d&&((b.body.style.overflow="hidden"),b.querySelectorAll("body > :not("+c+")").forEach(f=>f.setAttribute("inert",d)),(e.style.display="block"))};document.addEventListener("astro:after-swap",a);a()</script>
<script is:inline>function a(){let b=document,c="#modal-age-restricted",d="true",e=b.querySelector("body > "+c),f="ageVerified",g="searchParams",h=localStorage;new URL(b.location)[g].get(f)===d&&(h[f]=d);e&&(h[f]===d?b.querySelectorAll("a[href][data-age-restricted]").forEach(x=>{let y=new URL(x.href);y[g].set(f,d);x.href=y.href}):((b.body.style.overflow="hidden"),b.querySelectorAll("body > :not("+c+")").forEach(x=>x.setAttribute("inert",d)),(e.style.display="block")))};document.addEventListener("astro:after-swap",a);a()</script>

View file

@ -1,5 +1,5 @@
---
import SVGIcon from "../SVGIcon.astro";
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 64 64">
<path
d="m 19.478516,22.262 v 2.78125 h 8.347656 V 22.262 Z m 8.347656,2.78125 v 2.783203 h 2.783203 V 25.04325 Z m -8.347656,0 h -2.783204 v 5.566406 h 2.783204 z m 0,5.566406 v 2.78125 h 8.347656 v -2.78125 z m 8.347656,2.78125 v 5.566406 h 2.783203 v -5.566406 z m 0,5.566406 h -8.347656 v 2.78125 h 8.347656 z m -8.347656,0 v -2.783203 h -2.783204 v 2.783203 z M 36.173828,22.262 v 2.78125 h 8.347656 V 22.262 Z m 8.347656,2.78125 v 2.783203 h 2.783204 V 25.04325 Z m -8.347656,0 h -2.783203 v 5.566406 h 2.783203 z m 0,5.566406 v 2.78125 h 8.347656 v -2.78125 z m 8.347656,2.78125 v 5.566406 h 2.783204 v -5.566406 z m 0,5.566406 h -8.347656 v 2.78125 h 8.347656 z m -8.347656,0 v -2.783203 h -2.783203 v 2.783203 z M 50.087891,22.262 v 19.476562 h 2.78125 v -8.347656 h 8.347656 v 8.347656 H 64 V 22.262 h -2.783203 v 8.347656 H 52.869141 V 22.262 Z M 0,25.04325 v 2.783203 H 5.5644531 V 25.04325 Z m 5.5644531,2.783203 v 2.783203 h 5.5664059 v -2.783203 z m 5.5664059,2.783203 v 2.78125 h 2.78125 v -2.78125 z m 0,2.78125 H 5.5644531 v 2.783203 H 11.130859 Z M 5.5644531,36.174109 H 0 v 2.783203 h 5.5644531 z"
></path>
</SVGIcon>

View file

@ -1,4 +1,21 @@
import { parse as dateParse } from "date-fns";
import { parse } from "date-fns";
export const TOS_COMMISSION_STATUS: "CLOSED" | "OPEN" | "SEMI_OPEN" | "PRIVATE" = "OPEN";
export const TOS_UPDATED_AT: Date = dateParse("2024-07-11", "yyyy-MM-dd", 0);
type CommissionStatus = "CLOSED" | "OPEN" | "SEMI_OPEN" | "PRIVATE";
interface TosFeedItem {
status: CommissionStatus;
updatedAt: Date;
}
const parseISODate = (date: string) => parse(date, "yyyy-MM-dd", 0);
export const TOS_FEED: TosFeedItem[] = (
[
{ status: "CLOSED", updatedAt: parseISODate("2024-08-25") },
{ status: "OPEN", updatedAt: parseISODate("2024-07-11") },
] satisfies TosFeedItem[]
)
.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())
.slice(0, 5);
export const { status: TOS_COMMISSION_STATUS, updatedAt: TOS_UPDATED_AT } = TOS_FEED[0];

View file

@ -75,6 +75,7 @@ const title = pageTitle ? `${pageTitle} | Bad Manners` : "Bad Manners";
<span class="print:hidden" aria-hidden="true">&nbsp;|&nbsp;</span>
<a
href="/licenses.txt"
rel="license"
class="transition-colors hover:text-white hover:underline focus:text-white focus:underline motion-reduce:transition-none dark:hover:text-bm-300 dark:focus:text-bm-300 print:hidden"
>
Licenses

View file

@ -5,37 +5,36 @@ const htaccess = String.raw`
ErrorDocument 404 /404.html
RedirectMatch 301 ^/tos(/(index.html)?)?$ /terms_of_service/
RedirectMatch 301 ^/contact(/(index.html)?)?$ /
RedirectMatch 301 ^/@/(aryion|ekas?)\b https://aryion.com/g4/user/BadManners
RedirectMatch 301 ^/@/(bluesky|bsky)\b https://bsky.app/profile/badmanners.xyz
RedirectMatch 301 ^/@/(buymeacoffee|bmac)\b https://www.buymeacoffee.com/BadMannersXYZ
RedirectMatch 301 ^/@/(aryion|ekas?(portal)?)\b https://aryion.com/g4/user/BadManners
RedirectMatch 301 ^/@/(blue[\ _-]?sky|bsky)\b https://bsky.app/profile/badmanners.xyz
RedirectMatch 301 ^/@/(buymeacoffee|buy\ me\ a\ coffee|buy_me_a_coffee|buy-me-a-coffee|bmac)\b https://www.buymeacoffee.com/BadMannersXYZ
RedirectMatch 301 ^/@/carrd\b https://badmanners.carrd.co
RedirectMatch 301 ^/@/codeberg\b https://codeberg.org/BadManners
RedirectMatch 301 ^/@/cohost\b https://cohost.org/BadManners
RedirectMatch 301 ^/@/(fur[_-]?affinity|fa)\b https://www.furaffinity.net/user/badmanners
RedirectMatch 301 ^/@/gallery\b https://gallery.badmanners.xyz
RedirectMatch 301 ^/@/(github|gh)\b https://github.com/BadMannersXYZ
RedirectMatch 301 ^/@/gitlab\b https://gitlab.com/Bad_Manners
RedirectMatch 301 ^/@/gumroad\b https://badmanners.gumroad.com
RedirectMatch 301 ^/@/(inkbunny|ib)\b https://inkbunny.net/BadManners
RedirectMatch 301 ^/@/code[\ _-]?berg\b https://codeberg.org/BadManners
RedirectMatch 301 ^/@/co[\ _-]?host\b https://cohost.org/BadManners
RedirectMatch 301 ^/@/(fur[\ _-]?affinity|fa)\b https://www.furaffinity.net/user/badmanners
RedirectMatch 301 ^/@/(gallery|stor(y|ies)|games?)\b https://gallery.badmanners.xyz
RedirectMatch 301 ^/@/(git[\ _-]?hub|gh)\b https://github.com/BadMannersXYZ
RedirectMatch 301 ^/@/git[\ _-]?lab\b https://gitlab.com/Bad_Manners
RedirectMatch 301 ^/@/gum[\ _-]?road\b https://badmanners.gumroad.com
RedirectMatch 301 ^/@/(ink[\ _-]?bunny|ib)\b https://inkbunny.net/BadManners
RedirectMatch 301 ^/@/itaku\b https://itaku.ee/profile/badmanners
RedirectMatch 301 ^/@/itch\b https://bad-manners.itch.io
RedirectMatch 301 ^/@/keybase\b https://keybase.io/badmanners
RedirectMatch 301 ^/@/ko[_-]?fi\b https://ko-fi.com/badmanners
RedirectMatch 301 ^/@/(mastodon|meow\.social|gulp\.cafe)\b https://meow.social/@BadManners
RedirectMatch 301 ^/@/neocities\b https://badmanners.neocities.org
RedirectMatch 301 ^/@/ko[\ ._-]?fi\b https://ko-fi.com/badmanners
RedirectMatch 301 ^/@/(mastodon|meow[\ ._-]?social|gulp[\ ._-]?cafe)\b https://meow.social/@BadManners
RedirectMatch 301 ^/@/neo[\ _-]?cities\b https://badmanners.neocities.org
RedirectMatch 301 ^/@/picarto\b https://www.picarto.tv/BadManners
RedirectMatch 301 ^/@/pillowfort\b https://www.pillowfort.social/BadManners
RedirectMatch 301 ^/@/reddit\b https://www.reddit.com/user/BadManners_
RedirectMatch 301 ^/@/pillow[\ _-]?fort\b https://www.pillowfort.social/BadManners
RedirectMatch 301 ^/@/redd\.?it\b https://www.reddit.com/user/BadManners_
RedirectMatch 301 ^/@/signal\b https://signal.me/#eu/ytt_rk0fFmAB2JAW-x2PbUiJyc_H3kYmfL_Pq4QNh5QIDsiFtjdFHaqFRs1D36tB
RedirectMatch 301 ^/@/(sofurry|sf)\b https://bad-manners.sofurry.com
RedirectMatch 301 ^/@/ssh\b https://badmanners.xyz/ssh.pub
RedirectMatch 301 ^/@/steam\b https://steamcommunity.com/id/badmanners_
RedirectMatch 301 ^/@/subscribestar\b https://subscribestar.adult/bad-manners
RedirectMatch 301 ^/@/(so[\ _-]?furry|sf)\b https://bad-manners.sofurry.com
RedirectMatch 301 ^/@/steam([\ _-]?(community|powered))?\b https://steamcommunity.com/id/badmanners_
RedirectMatch 301 ^/@/subscribe[\ _-]?star\b https://subscribestar.adult/bad-manners
RedirectMatch 301 ^/@/(telegram|t\.me)\b https://t.me/bad_manners
RedirectMatch 301 ^/@/twitch\b https://www.twitch.tv/bad__manners
RedirectMatch 301 ^/@/weasyl\b https://www.weasyl.com/~badmanners
RedirectMatch 301 ^/@/(x|twitter)\b https://x.com/BadManners__
RedirectMatch 301 ^/@/(youtu\.?be|yt)\b https://www.youtube.com/@BadMannersXYZ
RedirectMatch 301 ^/@/(you[\ _-]?tube|youtu\.be|yt)\b https://www.youtube.com/@BadMannersXYZ
`.trim();
export const getStaticPaths: GetStaticPaths = async () => {

View file

@ -46,6 +46,7 @@ import ImageSamStickerJuicebox from "../assets/images/sam_sticker_juicebox.webp"
<a
class="h-card u-url text-link transition-colors motion-reduce:transition-none"
href="https://olivecow.carrd.co/"
rel="nofollow"
target="_blank"
>
<span class="p-name underline">OliveCow</span>

View file

@ -1,27 +1,22 @@
import rss from "@astrojs/rss";
import type { APIRoute } from "astro";
import { set as dateSet, subMinutes } from "date-fns";
import { TOS_COMMISSION_STATUS, TOS_UPDATED_AT } from "../data/tos";
import { TOS_FEED } from "../data/tos";
export const GET: APIRoute = async ({ site }) => {
export const GET: APIRoute = ({ site }) => {
return rss({
title: "Bad Manners",
description: "Bad Manners status updates",
site: site!,
items: [
{
title: {
CLOSED: "Story commissions are closed.",
OPEN: "Story commissions are open!",
SEMI_OPEN: "Story commissions are semi-open, and I may accept them less frequently.",
PRIVATE: "Story commissions are private; they are only open to select commissioners.",
}[TOS_COMMISSION_STATUS],
link: "https://badmanners.xyz/terms_of_service",
pubDate: subMinutes(
dateSet(TOS_UPDATED_AT, { hours: 12, minutes: 0, seconds: 0 }),
TOS_UPDATED_AT.getTimezoneOffset(),
),
},
],
items: TOS_FEED.map(({ status, updatedAt }) => ({
title: {
CLOSED: "Story commissions are closed.",
OPEN: "Story commissions are open!",
SEMI_OPEN: "Story commissions are semi-open, and I may accept them less frequently.",
PRIVATE: "Story commissions are private; they are only open to select commissioners.",
}[status],
link: "https://badmanners.xyz/terms_of_service",
pubDate: subMinutes(dateSet(updatedAt, { hours: 12, minutes: 0, seconds: 0 }), updatedAt.getTimezoneOffset()),
})),
});
};

View file

@ -1,6 +1,7 @@
---
import BaseLayout from "../layouts/BaseLayout.astro";
import IconEnvelope from "../components/icons/IconEnvelope.astro";
import IconBriefcase from "../components/icons/IconBriefcase.astro";
import IconBluesky from "../components/icons/brands/IconBluesky.astro";
import IconCodeberg from "../components/icons/brands/IconCodeberg.astro";
import IconCohost from "../components/icons/brands/IconCohost.astro";
@ -21,6 +22,7 @@ import IconPicarto from "../components/icons/brands/IconPicarto.astro";
import IconReddit from "../components/icons/brands/IconReddit.astro";
import IconSignal from "../components/icons/brands/IconSignal.astro";
import IconSoFurry from "../components/icons/brands/IconSoFurry.astro";
import IconSSH from "../components/icons/IconSSH.astro";
import IconSteam from "../components/icons/brands/IconSteam.astro";
import IconSubscribeStar from "../components/icons/brands/IconSubscribeStar.astro";
import IconTelegram from "../components/icons/brands/IconTelegram.astro";
@ -28,7 +30,6 @@ import IconTwitch from "../components/icons/brands/IconTwitch.astro";
import IconWeasyl from "../components/icons/brands/IconWeasyl.astro";
import IconX from "../components/icons/brands/IconX.astro";
import IconYouTube from "../components/icons/brands/IconYouTube.astro";
import IconSSH from "../components/icons/brands/IconSSH.astro";
---
<BaseLayout>
@ -39,7 +40,7 @@ import IconSSH from "../components/icons/brands/IconSSH.astro";
/>
<article class="h-card" aria-label="Homepage of Bad Manners">
<h1 id="title-home" class="pb-4 text-3xl tracking-tight sm:text-5xl">
Hi, I'm <span class="p-name">Bad Manners</span>!
Hi, I'm <span class="p-name font-medium">Bad Manners</span>!
</h1>
<section>
<img
@ -48,7 +49,7 @@ import IconSSH from "../components/icons/brands/IconSSH.astro";
alt="Logo for Bad Manners"
class="u-logo mx-auto my-4 h-screen max-h-48 rounded-full transition-transform hover:scale-110 motion-reduce:transition-none motion-reduce:hover:scale-100 sm:max-h-72"
/>
<p class="p-note mt-6 sm:px-5 md:px-6">I'm a safe vore enthusiast, and a furry who occasionally writes stuff.</p>
<p class="p-note mt-6 sm:px-5 md:px-6">I'm a safe vore enthusiast, a furry programmer, and occasionally a writer.</p>
{
Astro.site ? (
<a href={Astro.site} class="u-url sr-only" aria-label="Permalink">
@ -78,6 +79,21 @@ import IconSSH from "../components/icons/brands/IconSSH.astro";
<p class="sr-only">E-mail address</p>
</a>
</li>
<li id="gallery">
<a
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://gallery.badmanners.xyz"
rel="me"
aria-label="Main gallery"
data-age-restricted
>
<IconBriefcase
height="1.75rem"
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
</a>
</li>
<li id="bluesky">
<a
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
@ -394,7 +410,6 @@ import IconSSH from "../components/icons/brands/IconSSH.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<p class="sr-only">SSH public key</p>
</a>
</li>
<li id="steam">

View file

@ -25,6 +25,7 @@ import ImageSamRefsheet from "../assets/images/sam_refsheet.webp";
<a
class="group mx-auto mb-1 block w-max max-w-full pb-0 sm:mb-3"
href="https://booru.badmanners.xyz/index.php?q=post/view/1"
data-age-restricted
target="_blank"
>
<Image
@ -40,6 +41,7 @@ import ImageSamRefsheet from "../assets/images/sam_refsheet.webp";
<a
class="h-card u-url text-link transition-colors motion-reduce:transition-none"
href="https://linktr.ee/Rimmi1357"
rel="nofollow"
target="_blank"
>
<span class="p-name underline">Rimmi</span>
@ -77,6 +79,7 @@ import ImageSamRefsheet from "../assets/images/sam_refsheet.webp";
<a
class="group mx-auto mb-1 block w-max max-w-full pb-0 sm:mb-3"
href="https://booru.badmanners.xyz/index.php?q=post/view/2"
data-age-restricted
target="_blank"
>
<Image
@ -92,6 +95,7 @@ import ImageSamRefsheet from "../assets/images/sam_refsheet.webp";
<a
class="h-card u-url text-link transition-colors motion-reduce:transition-none"
href="https://olivecow.carrd.co/"
rel="nofollow"
target="_blank"
>
<span class="p-name underline">OliveCow</span>

View file

@ -22,10 +22,13 @@ import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar";
I've been a lurker in the furry vore community for a long time before I decided to start writing
<a
class="text-link transition-colors motion-reduce:transition-none"
href="https://gallery.badmanners.xyz/stories/1"><span class="underline">safe vore stories</span></a
href="https://gallery.badmanners.xyz/stories/1"
data-age-restricted><span class="underline">safe vore stories</span></a
>. Since there's a lot in furry endosoma that I enjoy, my stories tend to
<a class="text-link transition-colors motion-reduce:transition-none" href="https://gallery.badmanners.xyz/tags"
><span class="underline">have all sorts of themes and scenarios</span></a
<a
class="text-link transition-colors motion-reduce:transition-none"
href="https://gallery.badmanners.xyz/tags"
data-age-restricted><span class="underline">have all sorts of themes and scenarios</span></a
>, including exotic ones. My content is all tagged with the appropriate content warnings, so if you enjoy safe
vore, chances are that you'll find something in my gallery that is right up your alley.
</p>
@ -33,7 +36,8 @@ import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar";
I've also made a game called
<a
class="text-link transition-colors motion-reduce:transition-none"
href="https://gallery.badmanners.xyz/games/crossing-over"><span class="underline">"Crossing Over"</span></a
href="https://gallery.badmanners.xyz/games/crossing-over"
data-age-restricted><span class="underline">"Crossing Over"</span></a
>, as part of Strawberry Jam 8 (a game jam hosted by eevee on Feb. 2024). It's been my biggest project so far,
and I'm very proud of it! Check it out if you have an hour to spare and the subject matters interest you.
</p>
@ -46,6 +50,7 @@ import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar";
<a
class="u-url text-link mx-1 transition-colors motion-reduce:transition-none"
href="https://gallery.badmanners.xyz"
data-age-restricted
>
<IconBriefcase height="1.75rem" width="1.75rem" class="mr-1 inline" />
<span class="underline">gallery.badmanners.xyz</span>
@ -210,6 +215,7 @@ import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar";
<a
href="https://desiran.itch.io/wrangler"
target="_blank"
rel="nofollow"
class="text-link transition-colors motion-reduce:transition-none"
>
<span class="underline">Wrangler</span>