pwa-bundle icon indicating copy to clipboard operation
pwa-bundle copied to clipboard

Stimulus Components

Open Spomky opened this issue 1 year ago • 12 comments

Description

The demo contains helpful Stimulus Components and additional JS scripts that could be added here to ease the integration.

Example

  • [x] Connection status indicator: as the name suggests
  • [x] BackgroundSync Button: for non-idempotent requests that are normally redirected
  • [x] Sync Broadcast: indicates the number of remaining requests on the queue
  • [x] Prefetch-On-Demand: asks the SW to prefetch pages on demand (mouse over a link, an image...)
  • [x] Installation button: a button to install the app when the SW is ready
  • [ ] BackgroundFetch: with a simple action, trigger the download of data using BackgroundFetch API if available. Event and targets to show the progress

Spomky avatar Mar 02 '24 20:03 Spomky

While stimulus components would work, I think Twig Components would be even better (which would wrap the stimulus calls in twig).

tacman avatar Mar 02 '24 21:03 tacman

It's easier (for me at least) to work with the new bundle structure when adding stimulus/twig components. I see you've added #80 to milestone 1.1, would you like me to take a crack at refactoring the bundle config to use AbstractBundle?

tacman avatar Mar 02 '24 23:03 tacman

I made some progress on this, by creating a pwa-extra-bundle. I use Bootstrap rather than Tailwind, so I created twig components (which wrap stimulus controllers) where the content is configurable .

composer require survos/pwa-extra-bundle

Then cut, paste and customize

<twig:PwaInstall>
<twig:block name="install">
    <button type="button" class="btn  btn-primary">
        <span class="bi bi-download"></span>
        Install as PWA
    </button>
</twig:block>

<twig:block name="launch">
    <button type="button" class="btn btn-success">
        <span class="bi bi-download"></span>
        It's installed! Launch it!
    </button>
</twig:block>


</twig:PwaInstall>

<twig:ConnectionDetector>
    <twig:block name="online">
        <button class="btn btn-warning">Online</button>
    </twig:block>
    <twig:block name="offline">
        <button class="btn btn-danger">offline</button>
    </twig:block>
</twig:ConnectionDetector>

Launch still does not work, as I'm not sure what it's supposed to be.

See it in action: https://noise.survos.com/, functional but unattractive, as I'm in the middle of switch to bootstrap.

tacman avatar Mar 04 '24 19:03 tacman

Hi,

I started working on stimulus component. I am not familar with twig components and need to learn more about those. The first component I created is very simple, but allows customizing any HTML tag.

The refactoring of the bundle configuration is done in 1.1.x and I would like to release it by the end of the month.

Spomky avatar Mar 05 '24 18:03 Spomky

Two stimulus components are available:

@pwa/connection-status

Shows if the browser is online or not

@pwa/backgroundsync-form

When you submit a form offline and the response usually redirects to another page, the browser will go to the action url and not the redirect one as no response is received. With this component, the developers can easily redirect to the correct page even if offline. Custom behavior will be possible soon by intercepting events.

Spomky avatar Mar 07 '24 10:03 Spomky

background sync sounds very exciting, but I'm not sure how to use it. Here's the form rendering to add a task in phpwa-demo. Should the stimulus controller go on the form itself, or can it go on a div that wraps the form?

And if the user is offline, what happens? I've been reading https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Offline_and_background_operation, and an example would be very helpful.

                    {{ form_start(form, {attr: {class: ' class="max-w-sm mx-auto"'}}) }}
                    <div class="mb-5">
                        <label for="name" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">
                            {{ form_label(form.name) }}
                        </label>
                        {{ form_widget(form.name, {attr: {class: 'block w-full p-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 text-base focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500'}}) }}
                    </div>
                    <button type="submit" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Add</button>
                    {{ form_end(form) }}

tacman avatar Mar 07 '24 11:03 tacman

To work with BackgroundSync, you first need to declare what routes/methods you want to capture. In the demo, I enabled POST requests to https://localhost:8000/ and https://localhost:8000/items/*.

And you are done! ... almost done. When the form is supposed to redirect to the same page, no problem. This is the case when you add a new item. When the form is supposed to redirect the user to another page, the application is unable to redirect as no response is received. It shows the fallback page (if any), but the URL is wrong. This is the case for e.g. removing items.

The purpose of this stimulus component is to help redirecting correctly. It is used by the other forms on the page where we expect a redirection to /. In short:

ok you are posting data but fetch failed (when the promise rejects, it's a network error) so I redirect you. the request is in good hands and will be submitted again.

Spomky avatar Mar 07 '24 16:03 Spomky

the request is in good hands and will be submitted again.

So the idea is that it would return to a page that said "Thanks for submitting! You're offline now, but your request is queued for delivery when you reconnect."

That's pretty cool!

tacman avatar Mar 07 '24 16:03 tacman

That's the idea.

What I am trying to do now is to notify the frontend that the queue is not empty. The BackgroundSync Plugin accepts an onSync property, but it does not work as expected:

    "onSync": async ({queue}) => {
        await queue.replayRequests();
        remainingRequests = await queue.getAll();
        const bc = new BroadcastChannel('background-sync');
        bc.postMessage({name: '{$sync->queueName}', remaining: remainingRequests.length});
        bc.close();
    }

The final goal is to ease features like this one: https://github.com/GoogleChrome/workbox/issues/2044#issue-436802366 Having a BackgroundSync-aware component would be great.

Spomky avatar Mar 07 '24 16:03 Spomky

@pwa/sync-broadcast

Sync Broadcast Stimulus Component in action. The UX is ugly and need to refresh the page. But requests are correctly queued and if asked the number of requests in the queue is send through the Broadcast system. 2024-03-07_21h53_12

Spomky avatar Mar 07 '24 20:03 Spomky

This is great.

Question: is it possible to bypass the form submit and go directly to the queue? In particular, I'm thinking about when you want to take photos quickly and don't want to wait for the upload.

tacman avatar Mar 08 '24 00:03 tacman

It should be possible to bypass it using JS. With BackgroundSync, the strategy is NetworkOnly + the queue plugin i.e. will try to fetch the server and then fallback to the queue. By default the timeout is 3 seconds. It is possible to lower this value. I added an option for the NetworkFirst strategy and the page navigation, but missed it for the background sync feature. I will add it.

Spomky avatar Mar 08 '24 07:03 Spomky