feat: complete v1 — QR invoice generator with i18n, short URLs, offline support

- XMR address validation (standard, subaddress, integrated)
- Amount in XMR/EUR/USD/CHF with CoinGecko conversion
- QR code generation with monero: URI
- Shareable short URLs (/s/abc123) via self-hosted PHP backend
- i18n (DE/EN) with browser language detection
- Service worker for offline capability
- Dark mode, responsive design
This commit is contained in:
Alexander Schmidt
2026-03-24 16:38:44 +01:00
parent 5a088f595b
commit bd796e46dc
9 changed files with 1190 additions and 17 deletions

58
sw.js Normal file
View File

@@ -0,0 +1,58 @@
var CACHE_NAME = 'xmrpay-v1';
var ASSETS = [
'/',
'/index.html',
'/app.js',
'/i18n.js',
'/style.css',
'/lib/qrcode.min.js'
];
self.addEventListener('install', function (e) {
e.waitUntil(
caches.open(CACHE_NAME).then(function (cache) {
return cache.addAll(ASSETS);
})
);
self.skipWaiting();
});
self.addEventListener('activate', function (e) {
e.waitUntil(
caches.keys().then(function (names) {
return Promise.all(
names.filter(function (n) { return n !== CACHE_NAME; })
.map(function (n) { return caches.delete(n); })
);
})
);
self.clients.claim();
});
self.addEventListener('fetch', function (e) {
var url = new URL(e.request.url);
// CoinGecko API — network only, don't cache
if (url.hostname !== location.hostname) {
e.respondWith(fetch(e.request));
return;
}
// App assets — cache first, fallback to network
e.respondWith(
caches.match(e.request).then(function (cached) {
var networkFetch = fetch(e.request).then(function (response) {
if (response.ok) {
var clone = response.clone();
caches.open(CACHE_NAME).then(function (cache) {
cache.put(e.request, clone);
});
}
return response;
}).catch(function () {
return cached;
});
return cached || networkFetch;
})
);
});