mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-11-06 10:42:22 +01:00
Prerequisite for string extraction. formatTime() existed twice with subtly
different behaviour, and both hardcoded 'Yesterday ' — a string that would have
survived a naive extraction pass and stayed English forever.
app/static/js/datetime-utils.js now owns the today/yesterday/older logic, with
options for the two variants that actually differed: app.js needs an absolute
date in archive views, dm.js needs the short "5 Aug" form because DM rows are
narrower. Both keep a thin local formatTime() wrapper, so their ~15 call sites
are untouched. formatTimeAgo() moves over as-is (it only ever existed in app.js).
repeater-manage.js's fmtInt() no longer forces toLocaleString('en-US'), so
thousands separators follow the reader's locale. Kept as a function declaration
rather than a const alias so it stays hoisted, like the code it replaced.
Policy, documented in the module header: numeric formatting follows the BROWSER
locale, only the words are translated. Tying the clock to the UI language would
flip a Polish operator to "09:53 AM" the moment they switched the interface to
English, and mesh operators want 24-hour time whatever language the menus are in.
First five real catalog entries: common.yesterday, just_now, minutes_ago,
hours_ago, days_ago. days_ago exercises the plural machinery — Polish needs
"1 dzień temu" / "3 dni temu" / "5 dni temu" where English has one form.
Verified in the browser in both languages: all four helpers global, both
formatTime wrappers still routing through the shared core, Polish plural
categories correct, and the clock and number formats provably unchanged by the
UI language. Live chat timestamps render identically to before.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
88 lines
3.3 KiB
JavaScript
88 lines
3.3 KiB
JavaScript
const CACHE_NAME = 'mc-webui-v10';
|
|
const ASSETS_TO_CACHE = [
|
|
'/',
|
|
'/static/css/style.css',
|
|
'/static/js/app.js',
|
|
'/static/js/dm.js',
|
|
'/static/js/contacts.js',
|
|
'/static/js/message-utils.js',
|
|
'/static/js/filter-utils.js',
|
|
'/static/js/i18n-runtime.js',
|
|
'/static/js/datetime-utils.js',
|
|
'/static/js/console.js',
|
|
'/static/images/android-chrome-192x192.png',
|
|
'/static/images/android-chrome-512x512.png',
|
|
// Bootstrap 5.3.2 (local)
|
|
'/static/vendor/bootstrap/css/bootstrap.min.css',
|
|
'/static/vendor/bootstrap/js/bootstrap.bundle.min.js',
|
|
// Bootstrap Icons 1.11.2 (local)
|
|
'/static/vendor/bootstrap-icons/bootstrap-icons.css',
|
|
'/static/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2',
|
|
'/static/vendor/bootstrap-icons/fonts/bootstrap-icons.woff',
|
|
// Emoji Picker Element 1.28.1 (local)
|
|
'/static/vendor/emoji-picker-element/index.js',
|
|
'/static/vendor/emoji-picker-element/picker.js',
|
|
'/static/vendor/emoji-picker-element/database.js',
|
|
'/static/vendor/emoji-picker-element-data/en/emojibase/data.json',
|
|
// Socket.IO client 4.x (local)
|
|
'/static/vendor/socket.io/socket.io.min.js',
|
|
// Console page
|
|
'/console'
|
|
];
|
|
|
|
// Install event - cache core assets
|
|
self.addEventListener('install', (event) => {
|
|
event.waitUntil(
|
|
caches.open(CACHE_NAME)
|
|
.then((cache) => cache.addAll(ASSETS_TO_CACHE))
|
|
.then(() => self.skipWaiting())
|
|
);
|
|
});
|
|
|
|
// Activate event - clean old caches
|
|
self.addEventListener('activate', (event) => {
|
|
event.waitUntil(
|
|
caches.keys().then((cacheNames) => {
|
|
return Promise.all(
|
|
cacheNames
|
|
.filter((name) => name !== CACHE_NAME)
|
|
.map((name) => caches.delete(name))
|
|
);
|
|
}).then(() => self.clients.claim())
|
|
);
|
|
});
|
|
|
|
// Fetch event - hybrid strategy:
|
|
// - Cache-first for vendor libraries (static, unchanging)
|
|
// - Cache-first for translation catalogs (URL is content-hashed, so it cannot go stale)
|
|
// - Network-first for app content (dynamic, needs updates)
|
|
//
|
|
// Note: /i18n/<lang>.<hash>.js is deliberately NOT in ASSETS_TO_CACHE — the hashed
|
|
// filename isn't known when this file is written. It gets cached on first fetch instead.
|
|
self.addEventListener('fetch', (event) => {
|
|
const url = new URL(event.request.url);
|
|
|
|
// Cache-first for vendor libraries (Bootstrap, Icons) and translation catalogs
|
|
if (url.pathname.includes('/static/vendor/') || url.pathname.startsWith('/i18n/')) {
|
|
event.respondWith(
|
|
caches.match(event.request)
|
|
.then((cachedResponse) => {
|
|
return cachedResponse || fetch(event.request)
|
|
.then((response) => {
|
|
// Cache the fetched vendor file for future use
|
|
return caches.open(CACHE_NAME).then((cache) => {
|
|
cache.put(event.request, response.clone());
|
|
return response;
|
|
});
|
|
});
|
|
})
|
|
);
|
|
} else {
|
|
// Network-first for everything else (app code, API calls, dynamic content)
|
|
event.respondWith(
|
|
fetch(event.request)
|
|
.catch(() => caches.match(event.request))
|
|
);
|
|
}
|
|
});
|