From d0ba5c6f30261915602e0004d50b04933c627c85 Mon Sep 17 00:00:00 2001 From: Lee Salminen Date: Mon, 4 Jul 2022 11:01:08 -0600 Subject: [PATCH] Improved support for Progressive Web Apps (PWA) - modify webmanifest to use LNBITS_SITE_TITLE and LNBITS_CUSTOM_LOGO env vars - modify webmanifest to have a more accurate description, start_url and set theme_color to match default lnbits theme - add service worker to cache requests (chrome requires a registered service worker to activate some pwa functionality) - move webmanifest to (chrome acts weird with it in the body) --- lnbits/core/static/js/service-worker.js | 71 +++++++++++++++++++++++++ lnbits/core/static/js/wallet.js | 7 +++ lnbits/core/templates/core/wallet.html | 2 +- lnbits/core/views/generic.py | 19 ++++--- 4 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 lnbits/core/static/js/service-worker.js diff --git a/lnbits/core/static/js/service-worker.js b/lnbits/core/static/js/service-worker.js new file mode 100644 index 00000000..3ff18194 --- /dev/null +++ b/lnbits/core/static/js/service-worker.js @@ -0,0 +1,71 @@ +// the cache version gets updated every time there is a new deployment +const CACHE_VERSION = 1; +const CURRENT_CACHE = `lnbits-${CACHE_VERSION}`; + +// these are the routes we are going to cache for offline support +const cacheFiles = [ + '/core/static/js/wallet.js', + '/core/static/js/extensions.js', +]; + +// on activation we clean up the previously registered service workers +self.addEventListener('activate', evt => + evt.waitUntil( + caches.keys().then(cacheNames => { + return Promise.all( + cacheNames.map(cacheName => { + if (cacheName !== CURRENT_CACHE) { + return caches.delete(cacheName); + } + }) + ); + }) + ) +); + +// on install we download the routes we want to cache for offline +self.addEventListener('install', evt => + evt.waitUntil( + caches.open(CURRENT_CACHE).then(cache => { + return cache.addAll(cacheFiles); + }) + ) +); + +// fetch the resource from the network +const fromNetwork = (request, timeout) => + new Promise((fulfill, reject) => { + const timeoutId = setTimeout(reject, timeout); + fetch(request).then(response => { + clearTimeout(timeoutId); + fulfill(response); + update(request); + }, reject); + }); + +// fetch the resource from the browser cache +const fromCache = request => + caches + .open(CURRENT_CACHE) + .then(cache => + cache + .match(request) + .then(matching => matching || cache.match('/offline/')) + ); + +// cache the current page to make it available for offline +const update = request => + caches + .open(CURRENT_CACHE) + .then(cache => + fetch(request).then(response => cache.put(request, response)) + ); + +// general strategy when making a request (eg if online try to fetch it +// from the network with a timeout, if something fails serve from cache) +self.addEventListener('fetch', evt => { + evt.respondWith( + fromNetwork(evt.request, 10000).catch(() => fromCache(evt.request)) + ); + evt.waitUntil(update(evt.request)); +}); \ No newline at end of file diff --git a/lnbits/core/static/js/wallet.js b/lnbits/core/static/js/wallet.js index 29a1025d..05796893 100644 --- a/lnbits/core/static/js/wallet.js +++ b/lnbits/core/static/js/wallet.js @@ -702,3 +702,10 @@ new Vue({ ) } }) + +if (navigator.serviceWorker != null) { + navigator.serviceWorker.register('/service-worker.js') + .then(function(registration) { + console.log('Registered events at scope: ', registration.scope); + }); +} \ No newline at end of file diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html index db435866..e705f373 100644 --- a/lnbits/core/templates/core/wallet.html +++ b/lnbits/core/templates/core/wallet.html @@ -1,10 +1,10 @@ + {% extends "base.html" %} {% from "macros.jinja" import window_vars with context %} {% block scripts %} {{ window_vars(user, wallet) }} - {% endblock %} {% block title %} {{ wallet.name }} - {{ SITE_TITLE }} {% endblock %} diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index d9687e16..615de1e9 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -17,6 +17,7 @@ from lnbits.helpers import template_renderer, url_for from lnbits.settings import ( LNBITS_ADMIN_USERS, LNBITS_ALLOWED_USERS, + LNBITS_CUSTOM_LOGO, LNBITS_SITE_TITLE, SERVICE_FEE, ) @@ -251,6 +252,10 @@ async def lnurlwallet(request: Request): ) +@core_html_routes.get("/service-worker.js", response_class=FileResponse) +async def service_worker(): + return FileResponse("lnbits/core/static/js/service-worker.js") + @core_html_routes.get("/manifest/{usr}.webmanifest") async def manifest(usr: str): user = await get_user(usr) @@ -258,21 +263,21 @@ async def manifest(usr: str): raise HTTPException(status_code=HTTPStatus.NOT_FOUND) return { - "short_name": "LNbits", - "name": "LNbits Wallet", + "short_name": LNBITS_SITE_TITLE, + "name": LNBITS_SITE_TITLE + " Wallet", "icons": [ { - "src": "https://cdn.jsdelivr.net/gh/lnbits/lnbits@0.3.0/docs/logos/lnbits.png", + "src": LNBITS_CUSTOM_LOGO if LNBITS_CUSTOM_LOGO else "https://cdn.jsdelivr.net/gh/lnbits/lnbits@0.3.0/docs/logos/lnbits.png", "type": "image/png", "sizes": "900x900", } ], - "start_url": "/wallet?usr=" + usr, - "background_color": "#3367D6", - "description": "Weather forecast information", + "start_url": "/wallet?usr=" + usr + "&wal=" + user.wallets[0].id, + "background_color": "#1F2234", + "description": "Bitcoin Lightning Wallet", "display": "standalone", "scope": "/", - "theme_color": "#3367D6", + "theme_color": "#1F2234", "shortcuts": [ { "name": wallet.name,