Skip to content

Commit

Permalink
Merge pull request #48 from elsirion/2023-11-service-worker
Browse files Browse the repository at this point in the history
feat: add service worker so PWA can be installed
  • Loading branch information
elsirion authored Nov 20, 2023
2 parents 32bed30 + 72304b8 commit b0dd885
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 16 deletions.
29 changes: 15 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ tokio = { version = "1.28.2", features = [ "rt", "sync", "time" ] }
tokio-stream = "0.1.14"
wasm-bindgen = "0.2.87"
wasm-bindgen-futures = "0.4.37"

web-sys = { version = "0.3.65", features = [ "Navigator", "Window", "ServiceWorkerContainer" ] }
gloo-storage = "0.3.0"

[patch.crates-io]
Expand Down
3 changes: 3 additions & 0 deletions Trunk.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[[hooks]]
stage = "post_build"
command = "./post_build.sh"
Binary file added favicon.ico
Binary file not shown.
4 changes: 4 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<meta name="version" content="{{buildVersion}}">
<link data-trunk rel="tailwind-css" href="./index.css"/>
<link data-trunk rel="rust" data-wasm-opt="z"/>
<link data-trunk rel="copy-dir" href="./assets/"/>
<link data-trunk rel="copy-file" href="./service-worker.js"/>
<link data-trunk rel="copy-file" href="./manifest.json"/>
<link data-trunk rel="copy-file" href="./favicon.ico"/>
</head>
<body></body>
</html>
File renamed without changes.
56 changes: 56 additions & 0 deletions post_build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env bash
set -e

appName="fedimint-leptos-test"
stylePrefix="index"
styleFormat="css"

# Extract build version
indexJsFile=$(find ./dist/.stage -iname "${appName}-*.js")
echo "Extracting build version from file: ${indexJsFile}"
regex="(.*)${appName}-(.*).js"
_src="${indexJsFile}"
while [[ "${_src}" =~ ${regex} ]]; do
buildVersion="${BASH_REMATCH[2]}"
_i=${#BASH_REMATCH}
_src=${_src:_i}
done
if [ -z "${buildVersion}" ]; then
echo "Could not determine build version!"
exit 1
fi
echo "Build-Version is: ${buildVersion}"

# Replace placeholder in service-worker.js
serviceWorkerJsFile=$(find ./dist/.stage -iname "service-worker.js")
echo "Replacing {{buildVersion}} placeholder in: ${serviceWorkerJsFile}"
sed "s/{{buildVersion}}/${buildVersion}/g" "${serviceWorkerJsFile}" > "${serviceWorkerJsFile}.modified"
mv -f "${serviceWorkerJsFile}.modified" "${serviceWorkerJsFile}"

# Replace placeholder in index.html
indexHtmlFile=$(find ./dist/.stage -iname "index.html")
echo "Replacing {{buildVersion}} placeholder in: ${indexHtmlFile}"
sed "s/{{buildVersion}}/${buildVersion}/g" "${indexHtmlFile}" > "${indexHtmlFile}.modified"
mv -f "${indexHtmlFile}.modified" "${indexHtmlFile}"

# Extract CSS build version
indexJsFile=$(find ./dist/.stage -iname "${stylePrefix}-*.${styleFormat}")
echo "Extracting style build version from file: ${indexJsFile}"
regex="(.*)${stylePrefix}-(.*).${styleFormat}"
_src="${indexJsFile}"
while [[ "${_src}" =~ ${regex} ]]; do
cssBuildVersion="${BASH_REMATCH[2]}"
_i=${#BASH_REMATCH}
_src=${_src:_i}
done
if [ -z "${cssBuildVersion}" ]; then
echo "Could not determine style build version!"
exit 1
fi
echo "CSS Build-Version is: ${cssBuildVersion}"

# Replace placeholder in service-worker.js
serviceWorkerJsFile=$(find ./dist/.stage -iname "service-worker.js")
echo "Replacing {{cssBuildVersion}} placeholder in: ${serviceWorkerJsFile}"
sed "s/{{cssBuildVersion}}/${cssBuildVersion}/g" "${serviceWorkerJsFile}" > "${serviceWorkerJsFile}.modified"
mv -f "${serviceWorkerJsFile}.modified" "${serviceWorkerJsFile}"
81 changes: 81 additions & 0 deletions service-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
var buildVersion = "{{buildVersion}}"
var cssBuildVersion = "{{cssBuildVersion}}"
var cacheName = "webimint";

var filesToCache = [
'./',
'./index.html',
'./manifest.json',
'./fedimint-leptos-test-' + buildVersion + '_bg.wasm',
'./fedimint-leptos-test-' + buildVersion + '.js',
'./index-' + cssBuildVersion + '.css',
'./assets/icons/android-icon-192x192.png',
'./assets/icons/favicon-32x32.png',
'./assets/icons/favicon-96x96.png',
'./assets/icons/favicon-16x16.png',
'./favicon.ico',

// TODO: Add files you want the SW to cache. Rename entries to match your build output!
];

/* Start the service worker and cache all of the app's content */
self.addEventListener('install', function (event) {
console.log("Installing service-worker for build", buildVersion);
const preCache = async () => {
get_cache().then(function (cache) {
// We clear the whole cache, as we do not know which resources were updated!
cache.keys().then(function (requests) {
for (let request of requests) {
cache.delete(request);
}
});
cache.addAll(filesToCache.map(url => new Request(url, { credentials: 'same-origin' })));
})
};
event.waitUntil(preCache);
});

self.addEventListener('message', function (messageEvent) {
if (messageEvent.data === "skipWaiting") {
console.log("Service-worker received skipWaiting event", buildVersion);
self.skipWaiting();
}
});

self.addEventListener('fetch', function (e) {
e.respondWith(cache_then_network(e.request));
});

async function get_cache() {
return caches.open(cacheName);
}

async function cache_then_network(request) {
const cache = await get_cache();
return cache.match(request).then(
(cache_response) => {
if (!cache_response) {
return fetch_from_network(request, cache);
} else {
return cache_response;
}
},
(reason) => {
return fetch_from_network(request, cache);
}
);
}

function fetch_from_network(request, cache) {
return fetch(request).then(
(net_response) => {
return net_response;
},
(reason) => {
console.error("Network fetch rejected. Falling back to ./index.html. Reason: ", reason);
return cache.match("./index.html").then(function (cache_root_response) {
return cache_root_response;
});
}
)
}
9 changes: 8 additions & 1 deletion src/components/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ use crate::utils::empty_view;
use anyhow::anyhow;
use leptos::*;
use leptos_meta::{Title, Meta, Link};
use leptos::SignalGet;
use tracing::info;
use crate::components::service_worker::ServiceWorker;

//
// App component
//
#[component]
pub fn App(cx: Scope) -> impl IntoView {
info!("Starting app...");

let client = ClientRpc::new();
provide_client_context(cx, client.clone());

Expand Down Expand Up @@ -54,13 +59,15 @@ pub fn App(cx: Scope) -> impl IntoView {
};

view! { cx,
<ServiceWorker path="./service-worker.js" />

<Title text="Webimint" />
<Meta name="viewport" content="width=device-width, initial-scale=0.75, user-scalable=0, interactive-widget=overlays-content" />
<Link rel="icon" type_="image/png" sizes="192x192" href="/assets/icons/android-icon-192x192.png" />
<Link rel="icon" type_="image/png" sizes="32x32" href="/assets/icons/favicon-32x32.png" />
<Link rel="icon" type_="image/png" sizes="96x96" href="/assets/icons/favicon-96x96.png" />
<Link rel="icon" type_="image/png" sizes="16x16" href="/assets/icons/favicon-16x16.png" />
<Link rel="manifest" href="/assets/manifest.json" />
<Link rel="manifest" href="/manifest.json" />
<Meta name="theme-color" content="#ffffff" />

<div class="h-[100dvh]">
Expand Down
1 change: 1 addition & 0 deletions src/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod submit_button;
pub mod submit_form;
pub mod tx_list;
pub mod wallet_selector;
pub mod service_worker;

pub use app::*;
pub use balance::*;
Expand Down
23 changes: 23 additions & 0 deletions src/components/service_worker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use leptos::{component, create_action, IntoView, Scope, window};
use tracing::{info, warn};
use crate::utils::empty_view;

#[component]
pub fn ServiceWorker(cx: Scope, #[prop(into)] path: String) -> impl IntoView {
let register_action = create_action(cx, move |script_url: &String| {
let script_url = script_url.to_owned();
async move {
info!("Registering service worker: {}", script_url);
let promise = window().navigator().service_worker().register(script_url.as_str());
if let Err(e) = wasm_bindgen_futures::JsFuture::from(promise)
.await {
warn!("Service worker registration failed: {:?}", e);
}
info!("Service worker registered");
}
});

register_action.dispatch(path);

empty_view()
}

0 comments on commit b0dd885

Please sign in to comment.