Make UI language, timestamps and live feedback consistent
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
<script lang="ts">
|
||||
import RelativeTime from '$ui/components/RelativeTime.svelte';
|
||||
import { gameTitle } from '$ui/server-labels.js';
|
||||
import BackLink from '$ui/components/BackLink.svelte';
|
||||
import { onMount, tick } from 'svelte';
|
||||
import { page } from '$app/stores';
|
||||
@@ -37,8 +39,7 @@
|
||||
const progressBytes = $derived(Math.min(totalBytes, acceptedBytes + sendingBytes));
|
||||
const transferring = $derived(uploadBusy && (phase === 'preparing' || phase === 'uploading'));
|
||||
const hostName = (id: string | null) => id ? hosts.find(host => host.id === id)?.name ?? id : 'Browser upload';
|
||||
const gameName = (game: 't5' | 't6') => game === 't5' ? 'Black Ops I (T5)' : 'Black Ops II (T6)';
|
||||
const uploadDate = (item: OverviewLibrary['versions'][number]) => new Date(item.publishedAt ?? item.createdAt).toLocaleString();
|
||||
const gameName = (game: 't5' | 't6') => game === 't5' ? 'Black Ops I' : 'Black Ops II';
|
||||
|
||||
async function responseBody(response: Response) {
|
||||
let body;
|
||||
@@ -102,7 +103,7 @@
|
||||
}
|
||||
if (disposed) return;
|
||||
if (status.status !== 'published') throw new Error(status.error || 'Verification did not finish. Retry this upload to continue.');
|
||||
phase = 'published'; folder = null; notice = `${game.toUpperCase()} game files are ready. Active hosts will download and verify the latest files automatically.`;
|
||||
phase = 'published'; folder = null; notice = `${gameTitle(game, true)} game files are ready. Active hosts will download and verify the latest files automatically.`;
|
||||
if (folderInput) folderInput.value = '';
|
||||
await load();
|
||||
}
|
||||
@@ -209,7 +210,7 @@
|
||||
{#if !canManage}<p class="muted">Administrators upload game files. Active hosts prepare verified copies automatically.</p>{/if}
|
||||
{#if canManage && showUpload}
|
||||
<section class="card" id="game-upload">
|
||||
<div class="heading"><h2>{phase === 'published' ? `${game.toUpperCase()} files uploaded` : uploadId ? `Continue ${game.toUpperCase()} upload` : `Upload ${game.toUpperCase()} game folder`}</h2><Button size="sm" variant="ghost" disabled={uploadBusy} onclick={() => showUpload = false}>Close</Button></div>
|
||||
<div class="heading"><h2>{phase === 'published' ? `${gameTitle(game, true)} files uploaded` : uploadId ? `Continue ${gameTitle(game, true)} upload` : `Upload ${gameTitle(game, true)} game folder`}</h2><Button size="sm" variant="ghost" disabled={uploadBusy} onclick={() => showUpload = false}>Close</Button></div>
|
||||
<p>Select your complete {gameName(game)} game folder, including its installed DLC. Once verified, this upload becomes the latest files and hosts download it automatically. Running servers keep using their existing files.</p>
|
||||
{#if uploadError}<p class="ui-error" role="alert">{uploadError}</p>{/if}
|
||||
<form onsubmit={startUpload}>
|
||||
@@ -227,7 +228,7 @@
|
||||
</div>{/if}
|
||||
<div class="upload-actions">
|
||||
{#if transferring}<Button onclick={pauseUpload} disabled={pauseRequested}>{pauseRequested ? 'Pausing…' : 'Pause upload'}</Button>
|
||||
{:else if phase !== 'verifying' && phase !== 'published'}<Button type="submit" variant="primary" disabled={uploadBusy || !folder}>{uploadId ? 'Resume upload' : `Upload ${game.toUpperCase()} files`}</Button>{/if}
|
||||
{:else if phase !== 'verifying' && phase !== 'published'}<Button type="submit" variant="primary" disabled={uploadBusy || !folder}>{uploadId ? 'Resume upload' : `Upload ${gameTitle(game, true)} files`}</Button>{/if}
|
||||
{#if phase === 'idle' || phase === 'uploading'}<span class="muted">Keep this page open while files transfer. Interrupted uploads can be resumed below.</span>{/if}
|
||||
</div>
|
||||
</form>
|
||||
@@ -241,13 +242,13 @@
|
||||
<section class="card">
|
||||
<div class="heading">
|
||||
<div><h2>{gameName(library.game)}</h2>{#if latest}<p class="muted">{formatGameBytes(latest.sizeBytes)} · {latest.fileCount.toLocaleString()} files</p>{/if}</div>
|
||||
{#if canManage}<Button size="sm" disabled={uploadBusy} onclick={() => newUpload(library)}>{latest ? `Update ${library.game.toUpperCase()} files` : `Upload ${library.game.toUpperCase()} files`}</Button>{/if}
|
||||
{#if canManage}<Button size="sm" disabled={uploadBusy} onclick={() => newUpload(library)}>{latest ? `Update ${gameTitle(library.game, true)} files` : `Upload ${gameTitle(library.game, true)} files`}</Button>{/if}
|
||||
</div>
|
||||
{#if latest}
|
||||
<div class="heading"><span class="muted">Latest verified upload</span><Badge variant={latest.caches.some(cache => cache.status === 'failed') ? 'danger' : latest.caches.length && latest.caches.every(cache => cache.status === 'ready') ? 'online' : 'neutral'}>{latest.caches.length ? `${latest.caches.filter(cache => cache.status === 'ready').length} / ${latest.caches.length} host copies ready` : 'No hosts'}</Badge></div>
|
||||
<GameFileHosts caches={latest.caches} {hosts} latest />
|
||||
{:else}
|
||||
<p class="muted">Upload the complete {library.game.toUpperCase()} game folder once. Hosts will download and verify it automatically as part of setup.</p>
|
||||
<p class="muted">Upload the complete {gameName(library.game)} game folder once. Hosts will download and verify it automatically as part of setup.</p>
|
||||
{/if}
|
||||
{#if unfinished.length}
|
||||
<div class="uploads">
|
||||
@@ -255,7 +256,7 @@
|
||||
{#each unfinished as item (item.id)}
|
||||
{@const browserUpload = !item.sourceHostId}
|
||||
<article class="upload-entry">
|
||||
<div class="heading"><p>Started {new Date(item.createdAt).toLocaleString()} · {formatGameBytes(item.sizeBytes)} · {item.fileCount.toLocaleString()} files</p><Badge variant={item.state === 'failed' ? 'danger' : 'info'}>{item.state === 'failed' ? 'Failed' : browserUpload ? item.id === uploadId && phase === 'verifying' ? 'Verifying' : 'Uploading' : 'Importing'}</Badge></div>
|
||||
<div class="heading"><p>Started <RelativeTime value={item.createdAt} /> · {formatGameBytes(item.sizeBytes)} · {item.fileCount.toLocaleString()} files</p><Badge variant={item.state === 'failed' ? 'danger' : 'info'}>{item.state === 'failed' ? 'Failed' : browserUpload ? item.id === uploadId && phase === 'verifying' ? 'Verifying' : 'Uploading' : 'Importing'}</Badge></div>
|
||||
{#if item.error}<p class="ui-error">{item.error}</p>{/if}
|
||||
{#if browserUpload}
|
||||
<p class="muted">Resume with the original unchanged folder. The latest files stay available while this upload completes.</p>
|
||||
@@ -266,14 +267,14 @@
|
||||
</div>
|
||||
{/if}
|
||||
{#if latest || history.length}<details class="library-details"><summary>Details</summary><div class="detail-content">
|
||||
{#if latest}<p class="muted">Uploaded {uploadDate(latest)}</p>{@render uploadDetails(latest)}{@render downloadControls(latest)}{/if}
|
||||
{#if latest}<p class="muted">Uploaded <RelativeTime value={latest.publishedAt ?? latest.createdAt} /></p>{@render uploadDetails(latest)}{@render downloadControls(latest)}{/if}
|
||||
{#if history.length}
|
||||
<details class="history">
|
||||
<summary>Previous uploads ({history.length})</summary>
|
||||
<p class="muted">Old host copies are removed once no instances use them. Controller files are removed after all host copies are gone; upload history is kept.</p>
|
||||
{#each history as item (item.id)}
|
||||
<article class="upload-entry">
|
||||
<h3>Uploaded {uploadDate(item)}</h3>
|
||||
<h3>Uploaded <RelativeTime value={item.publishedAt ?? item.createdAt} /></h3>
|
||||
{#if item.filesRemoved}<p class="muted">Files removed from controller · upload history retained</p>{:else if item.filesRemoving}<p class="muted">Removing unused controller files · upload history retained</p>{/if}
|
||||
<p>{formatGameBytes(item.sizeBytes)} · {item.fileCount.toLocaleString()} files</p>
|
||||
{@render uploadDetails(item)}
|
||||
|
||||
Reference in New Issue
Block a user