feat(site): redesigned /download, releases and links moved from GitHub to Forgejo
- /download: the jar as an in-game item (pixel heart in an inventory slot, vanilla-style tooltip with version, size and SHA-256, copy button), spec list, three install steps, release timeline
- releases read through a cached nginx proxy (/api/releases): Forgejo's API has no CORS headers
- README, footer and landing links point to git.wihuk.pro/boba/LoVisual (the GitHub repo they used does not exist)
- release workflow publishes through the Forgejo API and ships lovisual.jar.sha256
- fix i18n interpolation placeholders ({{x}}) in the download strings
This commit is contained in:
parent
fa08d50704
commit
2d96f28091
19 changed files with 360 additions and 143 deletions
|
|
@ -2,10 +2,12 @@ import { useQuery } from '@tanstack/react-query'
|
|||
import { useTranslation } from '@shared/i18n/react'
|
||||
import { Markdown } from '@shared/ui/Markdown'
|
||||
import { fetchReleases } from './api'
|
||||
import './download.css'
|
||||
|
||||
/**
|
||||
* Changelog feed. Markdown bodies go through our own renderer, which builds React
|
||||
* elements and never raw HTML, so a `<script>` or `onerror=` in a release body stays inert text.
|
||||
* Changelog timeline, newest first. Markdown bodies go through our own renderer,
|
||||
* which builds React elements and never raw HTML, so a `<script>` or `onerror=` in
|
||||
* a release body stays inert text.
|
||||
*/
|
||||
export function ReleaseNotes() {
|
||||
const { t, i18n } = useTranslation('download')
|
||||
|
|
@ -21,23 +23,29 @@ export function ReleaseNotes() {
|
|||
)
|
||||
}
|
||||
|
||||
const date = new Intl.DateTimeFormat(i18n.language, { dateStyle: 'medium' })
|
||||
const date = new Intl.DateTimeFormat(i18n.language, { dateStyle: 'long' })
|
||||
|
||||
return (
|
||||
<ol className="flex flex-col gap-8">
|
||||
{data.map((r) => (
|
||||
<li key={r.tag_name} className="flex flex-col gap-2">
|
||||
<div className="flex flex-wrap items-baseline gap-x-3 gap-y-1">
|
||||
<h3 className="font-display text-xl font-bold text-snow">{r.name || r.tag_name}</h3>
|
||||
<time className="font-code text-xs text-frost" dateTime={r.published_at}>
|
||||
{date.format(new Date(r.published_at))}
|
||||
</time>
|
||||
</div>
|
||||
<div className="lv-markdown text-[0.95rem] leading-relaxed text-snow/85">
|
||||
<Markdown>{r.body}</Markdown>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
<ol className="lv-rail flex flex-col gap-10">
|
||||
{data.map((r, i) => {
|
||||
const downloads = r.assets.reduce((sum, a) => sum + a.download_count, 0)
|
||||
return (
|
||||
<li key={r.tag_name} className="relative flex flex-col gap-2">
|
||||
<span className="lv-dot" data-latest={i === 0} aria-hidden="true" />
|
||||
<div className="flex flex-wrap items-baseline gap-x-3 gap-y-1">
|
||||
<h3 className="font-display text-xl font-bold text-snow">{r.name || r.tag_name}</h3>
|
||||
{i === 0 ? <span className="rounded-full bg-ice/15 px-2 py-0.5 text-xs font-bold text-ice">{t('notes.latest')}</span> : null}
|
||||
<time className="font-code text-xs text-frost" dateTime={r.published_at}>
|
||||
{date.format(new Date(r.published_at))}
|
||||
</time>
|
||||
{downloads > 0 ? <span className="font-code text-xs text-frost">{t('notes.downloads', { count: downloads })}</span> : null}
|
||||
</div>
|
||||
<div className="lv-markdown text-[0.95rem] leading-relaxed text-snow/85">
|
||||
<Markdown>{r.body}</Markdown>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ol>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue