feat: explicit PWA update prompt with build stamp
- registerType 'prompt' + skipWaiting/clientsClaim false: new SW waits instead of silently hijacking running sessions - main.ts registers via virtual:pwa-register; onNeedRefresh shows dismissible toast (i18n en/ru/cu) with Update button -> updateSW(true) - No polling — update check only on navigation, zero extra traffic - Settings About shows __BUILD_DATE__ build stamp for remote debugging
This commit is contained in:
1 parent
a005e386a6
commit
f49735b360
5 files changed
+50
-4
No files matched your search
@@ -12,6 +12,10 @@ export interface Translations {
|
||||
error: string;
|
||||
notAvailable: string;
|
||||
|
||||
// App update
|
||||
updateAvailable: string;
|
||||
updateReload: string;
|
||||
|
||||
// Navigation
|
||||
nav: {
|
||||
calendar: string;
|
||||
@@ -548,6 +552,9 @@ const translations: Record<LanguageCode, Translations> = {
|
||||
error: 'Error',
|
||||
notAvailable: 'Not available',
|
||||
|
||||
updateAvailable: 'New version available',
|
||||
updateReload: 'Update',
|
||||
|
||||
nav: {
|
||||
calendar: '📅 Calendar',
|
||||
service: '📖 Service',
|
||||
@@ -1116,6 +1123,9 @@ const translations: Record<LanguageCode, Translations> = {
|
||||
error: 'Ошибка',
|
||||
notAvailable: 'Недоступно',
|
||||
|
||||
updateAvailable: 'Достꙋ́пна новаѧ вєрсі́ѧ',
|
||||
updateReload: 'Обнови́ти',
|
||||
|
||||
nav: {
|
||||
calendar: '📅 Календарь',
|
||||
service: '📖 Устав',
|
||||
@@ -1684,6 +1694,9 @@ const translations: Record<LanguageCode, Translations> = {
|
||||
error: 'Ошибка',
|
||||
notAvailable: 'Недоступно',
|
||||
|
||||
updateAvailable: 'Доступна новая версия',
|
||||
updateReload: 'Обновить',
|
||||
|
||||
nav: {
|
||||
calendar: '📅 Календарь',
|
||||
service: '📖 Уставъ',
|
||||
|
||||
+27
@@ -1,5 +1,32 @@
|
||||
import './styles/main.css';
|
||||
import { registerSW } from 'virtual:pwa-register';
|
||||
import { App } from './ui/app';
|
||||
import { getTranslations, type LanguageCode } from './core/i18n';
|
||||
import { loadSettings } from './ui/settings-view';
|
||||
|
||||
const updateSW = registerSW({
|
||||
immediate: true,
|
||||
onNeedRefresh: () => showUpdateToast(),
|
||||
});
|
||||
|
||||
function showUpdateToast() {
|
||||
if (document.getElementById('pwa-update-toast')) return;
|
||||
const t = getTranslations(loadSettings().language as LanguageCode);
|
||||
const toast = document.createElement('div');
|
||||
toast.id = 'pwa-update-toast';
|
||||
toast.className = 'fixed bottom-4 left-1/2 -translate-x-1/2 z-[60] bg-surface border border-gold/40 rounded-lg shadow-xl px-4 py-3 flex items-center gap-3 max-w-[calc(100vw-2rem)]';
|
||||
toast.innerHTML = `
|
||||
<span class="text-sm text-navy whitespace-nowrap">${t.updateAvailable}</span>
|
||||
<button id="pwa-update-btn" class="bg-gold text-navy rounded px-3 py-1 text-sm font-bold hover:bg-gold-dark transition-colors whitespace-nowrap">${t.updateReload}</button>
|
||||
<button id="pwa-update-dismiss" class="text-navy-light hover:text-navy text-lg leading-none px-1" aria-label="✕">×</button>
|
||||
`;
|
||||
document.body.appendChild(toast);
|
||||
toast.querySelector('#pwa-update-btn')?.addEventListener('click', () => {
|
||||
(toast.querySelector('#pwa-update-btn') as HTMLButtonElement).disabled = true;
|
||||
updateSW(true);
|
||||
});
|
||||
toast.querySelector('#pwa-update-dismiss')?.addEventListener('click', () => toast.remove());
|
||||
}
|
||||
|
||||
const app = new App(document.getElementById('app')!);
|
||||
app.init();
|
||||
@@ -319,6 +319,7 @@ export class SettingsView {
|
||||
${t.settings.aboutLicense}
|
||||
</a>
|
||||
</p>
|
||||
<p class="text-[10px] text-navy-light mt-2 opacity-70">build ${__BUILD_DATE__}</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
Vendored
+3
@@ -1 +1,4 @@
|
||||
/// <reference types="vite/client" />
|
||||
/// <reference types="vite-plugin-pwa/client" />
|
||||
|
||||
declare const __BUILD_DATE__: string;
|
||||
Reference in new issue
Block a user