feat(web): list previous app builds on the download page
ci / api / api (push) Successful in 48s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 58s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m17s
ci / ts / ts (packages + web) (push) Successful in 2m1s
ci / db / db (push) Successful in 1m4s
ci / api / api (push) Successful in 48s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 58s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m17s
ci / ts / ts (packages + web) (push) Successful in 2m1s
ci / db / db (push) Successful in 1m4s
GET /app/clients now returns up to 8 Gitea releases per OS so users can download older installers while they still exist after prune. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
8eb6b36888
commit
39aaaed969
12 files changed
+625
-357
No files matched your search
@@ -12,7 +12,7 @@ import s from './auth.module.css';
|
||||
|
||||
/**
|
||||
* Публичная страница приложений (/download, /android): ссылку можно отправить человеку.
|
||||
* Сборки — последний релиз Gitea через сервер; первым предлагается клиент текущей ОС.
|
||||
* Сборки — до 8 последних релизов Gitea через сервер; первым предлагается клиент текущей ОС.
|
||||
*/
|
||||
export function AppDownloadPage() {
|
||||
const { t, i18n } = useTranslation(['auth', 'common']);
|
||||
@@ -43,7 +43,10 @@ export function AppDownloadPage() {
|
||||
{latest.isPending ? null : data ? (
|
||||
<>
|
||||
{primary ? (
|
||||
<DownloadButton client={detected!} href={primary.download_url} meta={`${t('app.version', { version: data.version })} · ${formatBytes(primary.size, i18n.language)}`} />
|
||||
<>
|
||||
<DownloadButton client={detected!} href={primary.download_url} meta={`${t('app.version', { version: primary.version ?? data.version })} · ${formatBytes(primary.size, i18n.language)}`} />
|
||||
<OlderVersions items={primary.older ?? []} />
|
||||
</>
|
||||
) : detected ? (
|
||||
<div className={s.hint}>{t('app.unavailable_platform', { platform: t(`app.platform.${detected}`) })}</div>
|
||||
) : null}
|
||||
@@ -72,12 +75,15 @@ export function AppDownloadPage() {
|
||||
{others.map((id) => {
|
||||
const file = files[id]!;
|
||||
return (
|
||||
<a key={id} className={s.downloadAlt} href={file.download_url} download={file.filename}>
|
||||
{t(`app.download.${id}`)}
|
||||
<span>
|
||||
{t('app.version', { version: data.version })} · {formatBytes(file.size, i18n.language)}
|
||||
</span>
|
||||
</a>
|
||||
<div key={id} className={s.otherBlock}>
|
||||
<a className={s.downloadAlt} href={file.download_url} download={file.filename}>
|
||||
{t(`app.download.${id}`)}
|
||||
<span>
|
||||
{t('app.version', { version: file.version ?? data.version })} · {formatBytes(file.size, i18n.language)}
|
||||
</span>
|
||||
</a>
|
||||
<OlderVersions items={file.older ?? []} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
@@ -91,6 +97,22 @@ export function AppDownloadPage() {
|
||||
);
|
||||
}
|
||||
|
||||
function OlderVersions({ items }: { items: { version: string; filename?: string; size: number; download_url: string }[] }) {
|
||||
const { t, i18n } = useTranslation(['auth', 'common']);
|
||||
if (items.length === 0) return null;
|
||||
return (
|
||||
<details className={s.older}>
|
||||
<summary>{t('app.older')}</summary>
|
||||
{items.map((b) => (
|
||||
<a key={b.version} className={s.downloadAlt} href={b.download_url} download={b.filename}>
|
||||
{t('app.version', { version: b.version })}
|
||||
<span>{formatBytes(b.size, i18n.language)}</span>
|
||||
</a>
|
||||
))}
|
||||
</details>
|
||||
);
|
||||
}
|
||||
|
||||
function DownloadButton({ client, href, meta }: { client: AppClient; href: string; meta: string }) {
|
||||
const { t } = useTranslation('auth');
|
||||
return (
|
||||
|
||||
@@ -244,6 +244,12 @@
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.otherBlock {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.otherTitle {
|
||||
margin: 8px 0 0;
|
||||
color: var(--text3);
|
||||
@@ -272,6 +278,20 @@
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.older {
|
||||
font-size: 13px;
|
||||
color: var(--text3);
|
||||
}
|
||||
|
||||
.older summary {
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.older .downloadAlt {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.link {
|
||||
align-self: center;
|
||||
}
|
||||
@@ -122,7 +122,13 @@ export function fakeApi(initial: FakeUser[]) {
|
||||
version_code: 1_030_099,
|
||||
notes_md: '',
|
||||
clients: [
|
||||
{ client: 'windows', filename: 'pipu-music_1.3.0_x64-setup.exe', size: 20_000_000, download_url: '/api/v1/app/download/windows/1.3.0' },
|
||||
{
|
||||
client: 'windows',
|
||||
filename: 'pipu-music_1.3.0_x64-setup.exe',
|
||||
size: 20_000_000,
|
||||
download_url: '/api/v1/app/download/windows/1.3.0',
|
||||
older: [{ version: '1.2.0', filename: 'pipu-music_1.2.0_x64-setup.exe', size: 19_000_000, download_url: '/api/v1/app/download/windows/1.2.0' }],
|
||||
},
|
||||
{ client: 'linux', filename: 'pipu-music_1.3.0_amd64.deb', size: 4_000_000, download_url: '/api/v1/app/download/linux/1.3.0' },
|
||||
{ client: 'macos', filename: 'pipu-music_1.3.0_aarch64.dmg', size: 22_000_000, download_url: '/api/v1/app/download/macos/1.3.0' },
|
||||
{ client: 'android', filename: 'pipu-music-1.3.0.apk', size: 12_000_000, sha256: 'ab'.repeat(32), download_url: '/api/v1/app/download/android/1.3.0' },
|
||||
|
||||
Reference in new issue
Block a user