Workbox: share_target data reloads my entire PWA even when I try to block the GET or POST request

Created on 29 Jun 2020  Â·  12Comments  Â·  Source: GoogleChrome/workbox

Library Affected:
workbox-sw, workbox-build, etc.

Browser & Platform:
E.g. Google Chrome v51.0.1 for Android, or "all browsers".

Issue or Feature Request Description:
Basically I have an app, which lets you write dialogue for games. It's open source, so you are welcome to have a look at what I have so far for it.
https://github.com/YarnSpinnerTool/YarnEditor
It's using webpack, so this is where the manifest is made
https://github.com/YarnSpinnerTool/YarnEditor/blob/master/webpack.config.js#L113
its also injecting some custom service worker logic here
https://github.com/YarnSpinnerTool/YarnEditor/blob/master/webpack.config.js#L171
which lives here
https://github.com/YarnSpinnerTool/YarnEditor/blob/master/src/sw-src.js

The app is at a stage now where workbox is caching it, so if you refresh the browser while offline, the app still loads.

I've been struggling with adding the share_target feature - or the ability to send data to the pwa from other apps.

My request here is for a way to prevent the share_target feature from completely obliterating my app's state. I want to have my app receive data and react to it, add to its state (such as sharing text or url populates a text field, but without affecting the active state of the app (opened document, edited data, etc etc).

Currently it can receive data here
https://github.com/YarnSpinnerTool/YarnEditor/blob/master/src/js/classes/app.js#L127
but doing so kind of negates the wholepoint of the feature- since it reloads the pwa and wipes the user's progress completely. The user is not adding to their existing data, because workbox reloaded the window and lost it.

I couldn't find any meaningful examples that fit my scenario. What is the best way to deal with this scenario on workbox, when you have a single window app (one index.html file) that holds various bits of its state in several different classes?

My experiments with re-routing so far have failed- since workbox completely seems to reload my app and I couldnt prevent a POST or a GET from reloading it. Can workbox intercept the request, get the data from it and stop it from reloading my app?
Or should I take a different strategy - where I am constantly caching my app's state so when workbox reloads it, I can get it back to where the user left it (opened file, opened node, etc etc)

In any case, sorry if this has been asked before. I looked at stackoverflow and various other places only to continue to scratch my head.

I want to be able to share a link with my app or share an image url (not the actual image), without wiping the app's state. What is the cleanest way for this to be approached?

Workbox can cache files - great! But can it also cache the app's entire state and let me load it when it decides to reload everything? Or am I supposed to write tons of ugly code now to accommodate for this problem. Ok maybe not tons, but wouldnt it be incredible to have an elegant solution for this? :) intercept and block the reload completely, instead of manually managing state caching. Or have the service worker handle state caching too?

Most helpful comment

I implemented it via GET + the app now handles saving its entire state to localStorage.

Unfortunately I could not find a way around gh-pages not allowing the POST method. All I wanted was to get the image's url as text, but apparently its not possible to do with GET as the phone will not list your app when you try to share an image with GET

All 12 comments

(I'm not sure that this is a Workbox question as much as it is a general question about using the Web Share Target Level 2 API. Workbox doesn't have any code specific to share targets.)

That being said, https://github.com/GoogleChrome/samples/tree/gh-pages/web-share is an example of using a Workbox-powered service worker to listen for incoming fetch events corresponding to Web Share Target Level 2.

The relevant bit of the service worker is:

https://github.com/GoogleChrome/samples/blob/c582722d63c630e5a6eebef9996889e33e53f0b7/web-share/src/js/service-worker.js#L26-L68

Does that give you a starting point that helps?

Thank you for sharing these- they give me a good example to follow :)
My question was about preserving the state of the app when the POST/GET request happens.

Wouldn't this response trigger a reload of the page, which will wipe its state in memory?
https://github.com/GoogleChrome/samples/blob/c582722d63c630e5a6eebef9996889e33e53f0b7/web-share/src/js/service-worker.js#L67
It is handling the caching of stuff coming in, but the refresh is obliterating the app's other states. Do I need to handle storing those states and then restoring them after the event or will this example prevent it from reloading the window in the first place?

You need to respond to the incoming HTTP POST request with _some_ sort of response, so perhaps a response with an HTTP 201 (or maybe HTTP 204) status would give you the behavior you want? You can create that inside your service worker with new Response('', {status: 201}).

(Again, this all gets into the specifics of browser behavior, and is not exactly related to Workbox's functionality.)

That doesnt seem to prevent it from reloading the app

Btw the pwa is hosted on
https://yarnspinnertool.github.io/YarnEditor/

so I am kind of forced to put /YarnEditor/ as the action in the manifest,otherwise it navigates it to https://yarnspinnertool.github.io
:(

here is what I tried (and failed)

import { precacheAndRoute } from 'workbox-precaching';
import { NetworkFirst } from 'workbox-strategies';
import { registerRoute } from 'workbox-routing';

console.log('Yarn\'s service worker is caching files');
registerRoute(/https:\/\/yarnspinnertool\.github\.io\/YarnEditor\//, new NetworkFirst());

const shareTargetHandler = async ({event}) => {
  // alert('SHARED!');

  // After the POST succeeds, dont redirect 201 or 204
  return new Response('/YarnEditor/', {status: 204});
};

registerRoute(
  '/YarnEditor/',
  shareTargetHandler,
  'GET'
);

precacheAndRoute(self.__WB_MANIFEST);

Btw I am kind of forced to do
return new Response('/YarnEditor/', {status: 204});

Otherwise the app doesnt load after installed and started

The pwa being hosted on github makes things tricky with the url, but also github prevents me from using POST, so I am limited to GET

If the service worker can't intercept the request and stop it from reloading the app, I will be kind of forced to cache the app's state and load it back after a share, I am kind of not thrilled to do that

Can you try using POST in your manifest.json, not GET? And for the action value, you should be able to use a "fake" URL that is purely intercepted by the service worker within the scope of your web app. So your share_target manifest configuration would look like:

{
  ...

  "share_target": {
    "action": "/YarnEditor/_share-target",
    "enctype": "multipart/form-data",
    "method": "POST",
    "params": {
      "files": [{
        "name": "text",
        "accept": [
          "text/*",
        ]
      }]
    }
  }
}

And then in your service worker:

const shareTargetHandler = async ({event}) => {
  const formData = await event.request.formData();
  const mediaFiles = formData.getAll('text');

  for (const mediaFile of mediaFiles) {
    // Do something with each shared text file.
  }

  return new Response('', {status: 201});
};

registerRoute(
  '/YarnEditor/_share-target',
  shareTargetHandler,
  'POST'
);

This seems to turn the app into a blank screen (promptly after showing the loading splash, suggesting a reload again) when you share to it. We are now getting a different behaviour, I suppose because the response is an empty string?

I tried with 201, 204 and 200, no luck

If we do find a way to prevent it from reloading, it would make for a fantastic addition to examples

Why cant we just do event.preventDefault() instead of returning a response? That gives us "Cant connect to the site"

So far it looks like workbox will always show the app's splash screen (it's dumping it from memory?) and then navigate to somewhere

what about https://developers.google.com/web/tools/workbox/modules/workbox-precaching#ignore_url_parameters
does this somehow play into wheter the app will reload?
I am caching the app with
registerRoute(/https:\/\/yarnspinnertool\.github\.io\/YarnEditor\//, new NetworkFirst());

As mentioned earlier, I'm not sure that this question is specific to Workbox—it's more a general question about using the Web Share Target Level 2 API to receive an incoming POST request without reloading the installed PWA. I'm throwing out ideas using Workbox's syntax since that's what you're using in your service worker, and since I've put together a sample app that also uses Workbox (but in my app, it responds with an HTTP 304 redirect which does reload the page).

It's possible that the way the Web Share Target Level 2 on Chrome for Android is implemented, it's not going to be possible to accept the incoming file without reloading—there's not really anything Workbox can do at that point. If none of the ideas I've thrown out there have helped, that really might be the case, and it would be better to rephrase this as a general question about the Web Share Target Level 2 API (maybe asked on the issue tracker for that spec) as opposed to something Workbox-specific.

Thank you for the help so far :) I learned a few new things about workbox in the process,which will come in handy for the app.

I opened a new issue about it at
https://github.com/w3c/web-share/issues/164
It will be interesting to see what they have to say

does workbox support launch events?
https://github.com/WICG/sw-launch/blob/master/explainer.md

will this work on it?

self.addEventListener('launch', event => {
  event.waitUntil(async () => {
    const allClients = await clients.matchAll();
    // If there isn't one available, open a new window.
    if (allClients.length === 0) {
      const client = await clients.openWindow(event.request.url);
      return;
    }

    const client = allClients[0];
    client.postMessage(event.request.url);
    client.focus();
  }());
});

Okay, glad that you were able to get some clarity there. I'm going to close this issue for now.

Once a service worker feature ships, we take a look at what value, if any, Workbox can offer on top of the underlying interfaces. In general, we've tried to write Workbox as an optional layer that you can use alongside "native" service worker code, like the snippet you posted above. So it's not really a question of whether Workbox supports Launch Events, but whether any browsers do...

...and Launch Events are not natively implemented in any browser at the moment. I believe there is still some uncertainty about whether the proposal in https://github.com/WICG/sw-launch/blob/master/explainer.md will move forward, or whether something else that accomplishes similar goals but with a different interface will end up being implemented.

I understand. Thank you for clearing that up for me. Also for taking the
time to explain the status of this feature. I will have to store the
app state to localStorage for now and reload it after a share. Just need to figure
out a clean way to do it :)
Its unfortunately dragged some legacy libs (knockout and jquery yuck)

On Mon, 6 Jul 2020, 19:12 Jeffrey Posnick, notifications@github.com wrote:

Closed #2557 https://github.com/GoogleChrome/workbox/issues/2557.

—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/GoogleChrome/workbox/issues/2557#event-3517107063,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/ABRRWVPIPCLN2USESAAQS4DR2IH2PANCNFSM4OLNVYMA
.

I implemented it via GET + the app now handles saving its entire state to localStorage.

Unfortunately I could not find a way around gh-pages not allowing the POST method. All I wanted was to get the image's url as text, but apparently its not possible to do with GET as the phone will not list your app when you try to share an image with GET

Was this page helpful?
0 / 5 - 0 ratings