jsakamoto / Toolbelt.Blazor.PWA.Updater

Provide "Update Now" UI and feature to your Blazor PWA that appears when the next version of one is available.
Mozilla Public License 2.0
136 stars 7 forks source link

UPDATE button does not seem to work / do anything #2

Closed nicolasHul closed 2 years ago

nicolasHul commented 2 years ago

So after adding the PWAUpdater to my MainLayout.razor, and setting the EnvironmentsForWork to Production AND Development, I am getting the 'new version available', though when I click the 'UPDATE' button, nothing happens. I also don't get any error messages whatsoever. It looks like it cant find it's JavaScript files / parts, though, when looking at the folder structure it references it correctly.

What also seemed a bit odd was that when it first loaded, it was completely un-styled. None of the default values were added, also setting the variables in the style.css file didn't do anything. Though I thought this could be some other CSS library interfering (MudBlazor or something else), so I just changed the styles by directly targeting the CSS classes.

I am running a PWA, Blazor WebAssembly CORE hosted application. Do I need to add the packages in the server project as well to get it to work..?

My service-worker.published.js is looking as follows:

// Caution! Be sure you understand the caveats before publishing an application with
// offline support. See https://aka.ms/blazor-offline-considerations

self.importScripts('./service-worker-assets.js');
self.addEventListener('install', event => event.waitUntil(onInstall(event)));
self.addEventListener('activate', event => event.waitUntil(onActivate(event)));
self.addEventListener('fetch', event => event.respondWith(onFetch(event)));
self.addEventListener('message', event => {
    if (event.data?.type === 'SKIP_WAITING') self.skipWaiting();
});

const cacheNamePrefix = 'offline-cache-';
const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`;
const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/ ];
const offlineAssetsExclude = [ /^service-worker\.js$/ ];

async function onInstall(event) {
    console.info('Service worker: Install');

    // Fetch and cache all matching items from the assets manifest
    const assetsRequests = self.assetsManifest.assets
        .filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url)))
        .filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url)))
        .map(asset => new Request(asset.url, { integrity: asset.hash }));
    await caches.open(cacheName).then(cache => cache.addAll(assetsRequests));
}

async function onActivate(event) {
    console.info('Service worker: Activate');

    // Delete unused caches
    const cacheKeys = await caches.keys();
    await Promise.all(cacheKeys
        .filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName)
        .map(key => caches.delete(key)));
}

async function onFetch(event) {
    let cachedResponse = null;
    if (event.request.method === 'GET') {
        // For all navigation requests, try to serve index.html from cache
        // If you need some URLs to be server-rendered, edit the following check to exclude those URLs
        const shouldServeIndexHtml = event.request.mode === 'navigate';

        const request = shouldServeIndexHtml ? 'index.html' : event.request;
        const cache = await caches.open(cacheName);
        cachedResponse = await cache.match(request);
    }

    return cachedResponse || fetch(event.request);
}

and my index.html:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>Schwartzmans - Projecten</title>
    <base href="/" />
    <link href="assets/css/styles.css" rel="stylesheet" />
    <link rel="icon" type="image/x-icon" href="favicon.ico">
    <link href="manifest.json" rel="manifest" />
    <link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
    <link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
</head>

<body>
    <app>Loading...</app>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">πŸ—™</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
    <script src="_content/Radzen.Blazor/Radzen.Blazor.js"></script>
    <script src="_content/MudBlazor/MudBlazor.min.js"></script>
    <script src="_content/Toolbelt.Blazor.PWA.Updater.Service/script.min.js"></script>
    <script src="assets/js/zebra/BrowserPrint-3.0.216.min.js"></script>
    <script src="assets/js/bixolon/bxlcommon.js"></script>
    <script src="assets/js/bixolon/bxllabel.js"></script>
    <script src="assets/js/JSFunctionsPrint.js"></script>
    <script src="assets/js/JSFunctions.js"></script>
    <script src="assets/js/JSGlobalProjectFunctions.js"></script>
    <!--<script>navigator.serviceWorker.register('service-worker.js');</script>-->

</body>

</html>
jsakamoto commented 2 years ago

@nicolasHul

and setting the EnvironmentsForWork to "Production" AND "Development",

I guess setting the EnvironmentsForWork parameter to include "Development" is the reason why there was no effect when you clicked the "Update now" button.

When you run the Blazor Wasm project on Visual Studio or .NET CLI, such as dotnet watch or dotnet run, the loading service worker script is ./wwwroot/service-worker.js, not ./wwwroot/service-worker.published.js.

Therefore, you also have to change your ./wwwroot/service-worker.js,

from:

self.addEventListener('fetch', () => { });

to:

self.addEventListener('fetch', () => { });
// πŸ‘‡ add this line.
self.addEventListener('message', event => { if (event.data?.type === 'SKIP_WAITING') self.skipWaiting() });

After doing the above, the "Update Now" button should work expectedly, I think.

jsakamoto commented 2 years ago

@nicolasHul

it was completely un-styled.

You also have to include the "isolated style" file of Blazor apps in your index.html.

The path of the "isolated style" file is {Assembly Name}.styles.css. For example, if the assembly name of your Blazor application is MyBlazorApp1, you should make the index.html of that app to be like the below:

<html>
  <head>
    ...
    <!-- πŸ‘‡ Add this "link" element. -->
    <link href="MyBlazorApp1.styles.css" rel="stylesheet">
    ...

Please see also the Microsoft Docs site the following link: https://docs.microsoft.com/en-us/aspnet/core/blazor/components/css-isolation?view=aspnetcore-6.0#css-isolation-bundling

If you don't prefer to use the Blazor CSS isolation feature, include the _content/Toolbelt.Blazor.PWA.Updater/Toolbelt.Blazor.PWA.Updater.bundle.scp.css file instead of {Assembly Name}.styles.css.

nicolasHul commented 2 years ago

@nicolasHul

it was completely un-styled.

You also have to include the "isolated style" file of Blazor apps in your index.html.

The path of the "isolated style" file is {Assembly Name}.styles.css. For example, if the assembly name of your Blazor application is MyBlazorApp1, you should make the index.html of that app to be like the below:

<html>
  <head>
    ...
    <!-- πŸ‘‡ Add this "link" element. -->
    <link href="MyBlazorApp1.styles.css" rel="stylesheet">
    ...

Please see also the Microsoft Docs site the following link: https://docs.microsoft.com/en-us/aspnet/core/blazor/components/css-isolation?view=aspnetcore-6.0#css-isolation-bundling

If you don't prefer to use the Blazor CSS isolation feature, include the _content/Toolbelt.Blazor.PWA.Updater/Toolbelt.Blazor.PWA.Updater.bundle.scp.css file instead of {Assembly Name}.styles.css.

This seemed to do the trick. The extra self.addEventListener('message', event => { if (event.data?.type === 'SKIP_WAITING') self.skipWaiting() }); for the development did not work, but after adding the <link href="MyBlazorApp1.styles.css" rel="stylesheet"> it did work.. Thank you!

jsakamoto commented 2 years ago

@nicolasHul

Thank you for reply!

The extra self.addEventListener('message', event => { if (event.data?.type === 'SKIP_WAITING') self.skipWaiting() }); for the development did not work

Yeah, I guess that change also remained in the "waiting to activate" status at first. Therefore it looked like that change had no effect. But in fact, the changed script was loaded correctly but just waiting for shutting down all browser instances once, I guess.

Please see also my latest article below:

Anyway, I'm happy for you because this problem has gone away. 😊