Ship LNURL mint pages, indexing UI, and reviews rewrite.
Add lnurl list/detail routes, OG fixtures, i18n strings, and the write/ index client flows so the site surfaces the new mint type end to end. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,23 @@
|
||||
---
|
||||
/**
|
||||
* The site footer: one component, every page, three zones.
|
||||
*
|
||||
* Zone one is four columns — who this is, then the two link groups people actually
|
||||
* navigate to, then the language control. Zone two is a thin bar with the copyright
|
||||
* and the credit, and nothing else: the moment anything else moves into that row it
|
||||
* stops being a signature and starts being a fourth navigation surface.
|
||||
*
|
||||
* What used to be here was four flat rows, the last of which was ~20 raw language
|
||||
* links wrapped over two lines. That wall is now the same switcher the header carries
|
||||
* (see LanguageSwitcher.astro, `placement="footer"`), so there is one language control
|
||||
* on the site in two places rather than two controls that happen to agree.
|
||||
*
|
||||
* Everything below is static per locale and prerenders. No hex, no npub, no counts,
|
||||
* nothing fetched: the footer is the one region of every page that must be identical
|
||||
* on the home page and on a mint page that failed to load.
|
||||
*/
|
||||
import LanguageSwitcher from './LanguageSwitcher.astro';
|
||||
import Moai from './Moai.astro';
|
||||
import { localePath, useI18n } from '../i18n';
|
||||
import { pageLocale } from '../i18n/paths';
|
||||
|
||||
@@ -9,23 +27,29 @@ const GITHUB = 'https://github.com/Azzamo-net/cashumints.space';
|
||||
const { locale } = pageLocale(Astro);
|
||||
const t = useI18n(locale);
|
||||
|
||||
/**
|
||||
* Two rows: the pages people navigate to, then the ones they go looking for. The
|
||||
* disclaimer sits in the second row here and again at the bottom of every mint page,
|
||||
* which is where the decision it is about actually gets made.
|
||||
/*
|
||||
* Route slugs stay English in every language and `localePath` adds the prefix, exactly
|
||||
* as the header does. See src/i18n/routing.ts for why that trade was made.
|
||||
*/
|
||||
const nav = [
|
||||
{ path: '/mints', label: t('footer.allMints') },
|
||||
{ path: '/fedimints', label: t('footer.allFedimints') },
|
||||
{ path: '/reviews', label: t('footer.allReviews') },
|
||||
{ path: '/wallets', label: t('footer.wallets') },
|
||||
{ path: '/about', label: t('footer.about') },
|
||||
const explore = [
|
||||
{ href: localePath('/mints', locale), label: t('footer.allMints') },
|
||||
{ href: localePath('/fedimints', locale), label: t('footer.allFedimints') },
|
||||
{ href: localePath('/lnurl-mints', locale), label: t('footer.allLnurlMints') },
|
||||
{ href: localePath('/reviews', locale), label: t('footer.allReviews') },
|
||||
{ href: localePath('/wallets', locale), label: t('footer.wallets') },
|
||||
];
|
||||
|
||||
const legal = [
|
||||
{ path: '/disclaimer', label: t('footer.disclaimer') },
|
||||
{ path: '/terms', label: t('footer.terms') },
|
||||
{ path: '/privacy', label: t('footer.privacy') },
|
||||
/*
|
||||
* GitHub sits in this column rather than beside the credit line because it is a place
|
||||
* to go, not a signature. It is the only external link in the columns, so it carries
|
||||
* `rel="noopener"` and nothing else needs to.
|
||||
*/
|
||||
const site = [
|
||||
{ href: localePath('/about', locale), label: t('footer.about'), external: false },
|
||||
{ href: GITHUB, label: 'GitHub', external: true },
|
||||
{ href: localePath('/terms', locale), label: t('footer.terms'), external: false },
|
||||
{ href: localePath('/privacy', locale), label: t('footer.privacy'), external: false },
|
||||
{ href: localePath('/disclaimer', locale), label: t('footer.disclaimer'), external: false },
|
||||
];
|
||||
|
||||
/*
|
||||
@@ -39,31 +63,65 @@ const madeBy = t('footer.madeBy', {
|
||||
});
|
||||
---
|
||||
|
||||
<footer>
|
||||
<div class="foot-inner">
|
||||
<span>{t('footer.copyright', { year: String(year) })}</span>
|
||||
<div class="links">
|
||||
<a href={GITHUB} rel="noopener">GitHub</a>
|
||||
{nav.map((link) => <a href={localePath(link.path, locale)}>{link.label}</a>)}
|
||||
<footer class="site-footer" role="contentinfo">
|
||||
<div class="foot-cols">
|
||||
{/*
|
||||
The brand column. The custodial warning lives here, under the description,
|
||||
because it is the most honest sentence on the site and it was previously an
|
||||
orphan floating at the bottom right of the last row. It is set in `--muted`
|
||||
rather than `--faint`: quiet, but a step louder than the link columns, which is
|
||||
the only typographic claim this footer makes about relative importance.
|
||||
*/}
|
||||
<div class="foot-brand">
|
||||
<a class="foot-mark" href={localePath('/', locale)}>
|
||||
<Moai size={20} />
|
||||
<span>Cashumints.space</span>
|
||||
</a>
|
||||
<p class="foot-desc">{t('footer.tagline')}</p>
|
||||
<p class="foot-warn">{t('footer.warning')}</p>
|
||||
</div>
|
||||
|
||||
{/*
|
||||
One nav landmark over both link columns, not one per column: a screen reader
|
||||
listing landmarks should find "Footer", not "Footer" twice. The language control
|
||||
carries its own nav, because it is a different kind of thing — it does not take
|
||||
you to another page, it takes you to this page again.
|
||||
*/}
|
||||
<nav class="foot-nav" aria-label={t('footer.nav')}>
|
||||
<div class="foot-col">
|
||||
<h2 class="foot-head">{t('footer.col.explore')}</h2>
|
||||
<ul>
|
||||
{explore.map((link) => (
|
||||
<li><a href={link.href}>{link.label}</a></li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="foot-col">
|
||||
<h2 class="foot-head">{t('footer.col.site')}</h2>
|
||||
<ul>
|
||||
{site.map((link) => (
|
||||
<li>
|
||||
<a href={link.href} rel={link.external ? 'noopener' : undefined}>{link.label}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="foot-col foot-lang">
|
||||
<h2 class="foot-head">{t('lang.label')}</h2>
|
||||
<LanguageSwitcher placement="footer" />
|
||||
</div>
|
||||
<span class="foot-right" set:html={madeBy} />
|
||||
</div>
|
||||
<div class="foot-inner foot-legal">
|
||||
<div class="links">
|
||||
{legal.map((link) => <a href={localePath(link.path, locale)}>{link.label}</a>)}
|
||||
</div>
|
||||
<LanguageSwitcher variant="inline" />
|
||||
<span class="foot-right foot-warn">{t('footer.warning')}</span>
|
||||
|
||||
{/*
|
||||
The bottom bar. Two items, and the discipline is that it stays two items: the
|
||||
copyright and who made it. Everything else that was ever tempted into this row now
|
||||
has a column above it.
|
||||
*/}
|
||||
<div class="foot-bar">
|
||||
<span>{t('footer.copyright', { year: String(year) })}</span>
|
||||
<span class="foot-credit" set:html={madeBy} />
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<style>
|
||||
/* The second row is quieter than the first and has no top border of its own: it
|
||||
reads as a continuation of the footer, not a second footer. */
|
||||
.foot-legal { padding-top: 0; border-top: none; font-size: 12.5px; }
|
||||
.foot-warn { color: var(--faint); }
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.foot-legal { padding-top: 4px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user