Show publication time in larger update version message
CI / verify (push) Waiting to run

This commit is contained in:
Sucukdeluxe
2026-09-22 06:25:00 +02:00
parent 4b5a445ae0
commit 8654cda7dc
8 changed files with 34 additions and 2 deletions
+1
View File
@@ -97,6 +97,7 @@ npm audit --omit=dev
## Zuletzt verifiziert
- Update-Dialog nach `2.1.49`: Versionsmeldung von 12 auf 14 px erhöht. Updater reicht `published_at` als `publishedAt` weiter; Anzeige ergänzt lokale Veröffentlichungszeit als `TT.MM.JJJJ - HH:mm`, fehlende/ungültige Werte bleiben ausgeblendet. Deutsche und englische Übersetzung mit Datum geprüft. 40 Übersetzungs-/Updater-Tests, zwei gezielte Renderer-Tests und Lint ohne Warnungen erfolgreich. Laufende Entwicklung zeigt im lokalen Test `Update v2.1.50 verfügbar (22.09.2026 - 19:30)`; kein Release veröffentlicht.
- Update-Button nach `2.1.49`: feste Breite für Button und sichtbaren Platzhalter von 146 auf 166 px erhöht; Beschriftung ohne Umbruch und ohne Schrumpfen. Gezielter Header-Regressionstest und Lint erfolgreich. In der laufenden Entwicklung für Deutsch und Englisch jeweils eine Textzeile und ausreichender Innenabstand per DOM-Messung bestätigt. Lokaler Anzeigetest für `2.1.50` im ignorierten Entwicklungslauncher aktiv; Download und Installation dort gesperrt, kein tatsächliches Release. Entwicklerversion automatisch neu gestartet.
- Nachbesserung gegen abgeschnittenes letztes Zeichen: gezielter Electron-Layouttest für beide Sprachen und sechs Breiten sowie Lint erfolgreich. Screenshot mit vollständigem Text geprüft; automatischer Entwicklungsneustart bestätigt. Kein Release.
- Einstellungssuche: auf Nutzerwunsch Lupe rechts (12 px Innenabstand), Suchtext mit symmetrischem 32-px-Padding zentriert. Gezielter Electron-Layouttest prüft Zentrierung, Symbolposition und vollständige Platzhalter in beiden Sprachen bei sechs Breiten; Lint erfolgreich, Screenshot visuell geprüft. Automatischer Entwicklungsneustart bestätigt; noch nicht veröffentlicht.
+1
View File
@@ -185,6 +185,7 @@ async function checkForUpdate(options = {}) {
currentVersion,
remoteVersion,
transportTag,
publishedAt: release.published_at || null,
releaseUrl: release.html_url,
assetUrl: setupAsset.browser_download_url,
assetSize: setupAsset.size,
+10 -1
View File
@@ -9558,6 +9558,14 @@ function _renderUpdateReleaseNotes(container, value) {
return container.childElementCount > 0;
}
function formatUpdatePublicationDate(value) {
if (!value) return '';
const date = new Date(value);
if (!Number.isFinite(date.getTime())) return '';
const pad = number => String(number).padStart(2, '0');
return `${pad(date.getDate())}.${pad(date.getMonth() + 1)}.${date.getFullYear()} - ${pad(date.getHours())}:${pad(date.getMinutes())}`;
}
function showUpdateBanner(info) {
if (!info) return;
_knownUpdateInfo = { ...info, available: true };
@@ -9575,7 +9583,8 @@ function showUpdateBanner(info) {
const installButton = document.getElementById('installUpdateBtn');
if (title) title.textContent = 'Eine neue Version ist verfügbar!';
if (message) {
message.textContent = `Update v${version} verfügbar`;
const publishedAt = formatUpdatePublicationDate(info.publishedAt);
message.textContent = `Update v${version} verfügbar${publishedAt ? ` (${publishedAt})` : ''}`;
message.hidden = false;
}
if (notes && notesBody) {
+2
View File
@@ -804,6 +804,7 @@
if (exact) return `${leading}${exact}${trailing}`;
const patterns = target === 'en'
? [
[/^Update v([\d.]+) verfügbar \(([^)]+)\)$/, 'Update v$1 available ($2)'],
[/^Update v(.+) verfügbar$/, 'Update v$1 available'],
[/^Wiederhergestellte Warteschlange startet in (.+) s \((.+) Jobs\)\.$/, 'Restored queue starts in $1 s ($2 jobs).'],
[/^Login ok, Upload-Form bereit \(Dateifeld: (.+)\)$/, 'Login successful, upload form ready (file field: $1)'],
@@ -912,6 +913,7 @@
[/^Update v(.+) verfügbar\. Klicken zum Installieren\.$/, 'Update v$1 available. Click to install.']
]
: [
[/^Update v([\d.]+) available \(([^)]+)\)$/, 'Update v$1 verfügbar ($2)'],
[/^Update v(.+) available$/, 'Update v$1 verfügbar'],
[/^Sleep in (\d+)s\.\.\.$/, 'Ruhezustand in $1s...'],
[/^Shut down in (\d+)s\.\.\.$/, 'Herunterfahren in $1s...'],
+1 -1
View File
@@ -2952,7 +2952,7 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
.update-dialog-copy p {
margin: 0;
color: var(--text-muted);
font-size: 12px;
font-size: 14px;
line-height: 1.55;
}
+2
View File
@@ -176,6 +176,8 @@ test('English is the fallback language and German remains selectable', () => {
test('translations cover static labels and interpolated status text in both languages', () => {
assert.equal(translateText('Einstellungen', 'en'), 'Settings');
assert.equal(translateText('Update v2.1.0 verfügbar', 'en'), 'Update v2.1.0 available');
assert.equal(translateText('Update v2.1.50 verfügbar (22.09.2026 - 19:30)', 'en'), 'Update v2.1.50 available (22.09.2026 - 19:30)');
assert.equal(translateText('Update v2.1.50 available (22.09.2026 - 19:30)', 'de'), 'Update v2.1.50 verfügbar (22.09.2026 - 19:30)');
assert.equal(translateText('Settings', 'de'), 'Einstellungen');
assert.equal(translateText('Update v2.1.0 available', 'de'), 'Update v2.1.0 verfügbar');
assert.equal(translateText('Alle Status', 'en'), 'Any status');
+15
View File
@@ -5483,6 +5483,21 @@ test('upload sidebar renders and updates the remaining upload size', () => {
assert.match(appSource, /_setUploadTelemetryText\(['"]uploadTelemetryRemainingSize['"],\s*formatBytes\(stats\.bytesRemaining\)\)/u);
});
test('update publication dates use local time and omit missing or invalid metadata', () => {
const vm = require('node:vm');
const source = fs.readFileSync(path.join(__dirname, '../renderer/app.js'), 'utf8');
const formatter = source.slice(source.indexOf('function formatUpdatePublicationDate('), source.indexOf('function showUpdateBanner('));
const context = vm.createContext({});
vm.runInContext(formatter, context);
assert.equal(vm.runInContext("formatUpdatePublicationDate('2026-09-22T19:30:00')", context), '22.09.2026 - 19:30');
for (const value of [null, undefined, '', 'invalid']) {
context.value = value;
assert.equal(vm.runInContext('formatUpdatePublicationDate(value)', context), '');
}
const css = fs.readFileSync(path.join(__dirname, '../renderer/styles.css'), 'utf8');
assert.match(css, /\.update-dialog-copy p\s*\{[^}]*font-size:\s*14px;/su);
});
test('header occupies its final geometry before asynchronous initialization', () => {
const projectRoot = path.join(__dirname, '..');
const html = fs.readFileSync(path.join(projectRoot, 'renderer', 'index.html'), 'utf8');
+2
View File
@@ -231,6 +231,7 @@ test('update preparation refreshes a cached release before downloading the insta
name: `Multi-Hoster-Upload v${version}`,
tag_name: `v${version}`,
html_url: `https://update.invalid/releases/${version}`,
published_at: '2026-09-22T17:30:00Z',
body: `Release ${version}`,
assets: [
{
@@ -288,6 +289,7 @@ test('update preparation refreshes a cached release before downloading the insta
};
const oldCheck = await isolatedUpdater.checkForUpdate();
assert.equal(oldCheck.remoteVersion, oldVersion);
assert.equal(oldCheck.publishedAt, '2026-09-22T17:30:00Z');
global.fetch = async url => {
const value = String(url);