Skip to content

Commit

Permalink
Add background fetch support to service worker
Browse files Browse the repository at this point in the history
This commit introduces background fetch functionality, including DTOs, service worker rules, and a Stimulus controller for handling fetch progress and completion. It also updates the service worker configuration to enable caching, progress, success URLs, and customizable messages for success and failure scenarios.
  • Loading branch information
Spomky committed Feb 10, 2025
1 parent 995cd68 commit 4d8f943
Show file tree
Hide file tree
Showing 7 changed files with 312 additions and 10 deletions.
7 changes: 7 additions & 0 deletions assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
"version": "1.0.0",
"symfony": {
"controllers": {
"backgroundfetch": {
"main": "src/backgroundfetch_controller.js",
"name": "pwa/backgroundfetch",
"webpackMode": "eager",
"fetch": "eager",
"enabled": true
},
"backgroundsync-form": {
"main": "src/backgroundsync-form_controller.js",
"name": "pwa/backgroundsync-form",
Expand Down
61 changes: 61 additions & 0 deletions assets/src/backgroundfetch_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
'use strict';

import { Controller } from '@hotwired/stimulus';

/* stimulusFetch: 'lazy' */
export default class extends Controller {
download = async ({params}) => {
const { id, requests, title, icons, downloadTotal } = params;
if (!requests) {
return;
}
const processId = id || self.crypto.randomUUID();
await this.fetch(processId, requests, {
title,
icons,
downloadTotal
});
}

fetch = async (id, requests, options) => {
try {
const registration = await navigator.serviceWorker.ready;
const bgFetch = await registration.backgroundFetch.fetch(id, requests, options);
bgFetch.addEventListener('progress', () => this.dispatchStatus(bgFetch));

} catch (error) {
this.dispatchEvent('error', { error });
}
}

status = async () => {
const registration = await navigator.serviceWorker.ready;
const ids = await registration.backgroundFetch.getIds();
this.dispatchEvent('ids', ids);
const promises = ids.map(async (id) => {
const bgFetch = await registration.backgroundFetch.get(id);
if (!bgFetch) {
return;
}
bgFetch.addEventListener('progress', () => this.dispatchStatus(bgFetch));
});
await Promise.all(promises);
}

dispatchStatus = (bgFetch) => {
this.dispatchEvent('status', {
id: bgFetch.id,
uploadTotal: bgFetch.uploadTotal,
uploaded: bgFetch.uploaded,
downloadTotal: bgFetch.downloadTotal,
downloaded: bgFetch.downloaded,
result: bgFetch.result,
failureReason: bgFetch.failureReason,
recordsAvailable: bgFetch.recordsAvailable,
});
}

dispatchEvent = (name, payload) => {
this.dispatch(name, { detail: payload });
}
}
80 changes: 70 additions & 10 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,18 @@ parameters:
count: 1
path: src/DataCollector/PwaCollector.php

-
message: '#^Class SpomkyLabs\\PwaBundle\\Dto\\BackgroundFetch has an uninitialized property \$cacheName\. Give it default value or assign it in the constructor\.$#'
identifier: property.uninitialized
count: 1
path: src/Dto/BackgroundFetch.php

-
message: '#^Class SpomkyLabs\\PwaBundle\\Dto\\BackgroundFetch has an uninitialized property \$enabled\. Give it default value or assign it in the constructor\.$#'
identifier: property.uninitialized
count: 1
path: src/Dto/BackgroundFetch.php

-
message: '#^Class SpomkyLabs\\PwaBundle\\Dto\\BackgroundSync has an uninitialized property \$forceSyncFallback\. Give it default value or assign it in the constructor\.$#'
identifier: property.uninitialized
Expand Down Expand Up @@ -1479,13 +1491,13 @@ parameters:
-
message: '#^Cannot call method append\(\) on mixed\.$#'
identifier: method.nonObject
count: 6
count: 8
path: src/Resources/config/definition/service_worker.php

-
message: '#^Cannot call method arrayNode\(\) on mixed\.$#'
identifier: method.nonObject
count: 17
count: 18
path: src/Resources/config/definition/service_worker.php

-
Expand Down Expand Up @@ -1515,7 +1527,7 @@ parameters:
-
message: '#^Cannot call method canBeEnabled\(\) on mixed\.$#'
identifier: method.nonObject
count: 1
count: 2
path: src/Resources/config/definition/service_worker.php

-
Expand All @@ -1527,7 +1539,7 @@ parameters:
-
message: '#^Cannot call method children\(\) on mixed\.$#'
identifier: method.nonObject
count: 11
count: 12
path: src/Resources/config/definition/service_worker.php

-
Expand All @@ -1539,7 +1551,7 @@ parameters:
-
message: '#^Cannot call method defaultNull\(\) on mixed\.$#'
identifier: method.nonObject
count: 6
count: 8
path: src/Resources/config/definition/service_worker.php

-
Expand All @@ -1557,13 +1569,13 @@ parameters:
-
message: '#^Cannot call method end\(\) on mixed\.$#'
identifier: method.nonObject
count: 107
count: 112
path: src/Resources/config/definition/service_worker.php

-
message: '#^Cannot call method example\(\) on mixed\.$#'
identifier: method.nonObject
count: 33
count: 36
path: src/Resources/config/definition/service_worker.php

-
Expand All @@ -1587,7 +1599,7 @@ parameters:
-
message: '#^Cannot call method info\(\) on mixed\.$#'
identifier: method.nonObject
count: 68
count: 71
path: src/Resources/config/definition/service_worker.php

-
Expand All @@ -1605,7 +1617,7 @@ parameters:
-
message: '#^Cannot call method isRequired\(\) on mixed\.$#'
identifier: method.nonObject
count: 5
count: 6
path: src/Resources/config/definition/service_worker.php

-
Expand All @@ -1617,7 +1629,7 @@ parameters:
-
message: '#^Cannot call method scalarNode\(\) on mixed\.$#'
identifier: method.nonObject
count: 37
count: 40
path: src/Resources/config/definition/service_worker.php

-
Expand Down Expand Up @@ -1872,6 +1884,54 @@ parameters:
count: 1
path: src/ServiceWorkerRule/AppendCacheStrategies.php

-
message: '#^Cannot access property \$cacheName on SpomkyLabs\\PwaBundle\\Dto\\BackgroundFetch\|null\.$#'
identifier: property.nonObject
count: 1
path: src/ServiceWorkerRule/BackgroundFetchCache.php

-
message: '#^Cannot access property \$enabled on SpomkyLabs\\PwaBundle\\Dto\\BackgroundFetch\|null\.$#'
identifier: property.nonObject
count: 1
path: src/ServiceWorkerRule/BackgroundFetchCache.php

-
message: '#^Cannot access property \$failureMessage on SpomkyLabs\\PwaBundle\\Dto\\BackgroundFetch\|null\.$#'
identifier: property.nonObject
count: 2
path: src/ServiceWorkerRule/BackgroundFetchCache.php

-
message: '#^Cannot access property \$progressUrl on SpomkyLabs\\PwaBundle\\Dto\\BackgroundFetch\|null\.$#'
identifier: property.nonObject
count: 4
path: src/ServiceWorkerRule/BackgroundFetchCache.php

-
message: '#^Cannot access property \$successMessage on SpomkyLabs\\PwaBundle\\Dto\\BackgroundFetch\|null\.$#'
identifier: property.nonObject
count: 2
path: src/ServiceWorkerRule/BackgroundFetchCache.php

-
message: '#^Cannot access property \$successUrl on SpomkyLabs\\PwaBundle\\Dto\\BackgroundFetch\|null\.$#'
identifier: property.nonObject
count: 4
path: src/ServiceWorkerRule/BackgroundFetchCache.php

-
message: '#^Unused SpomkyLabs\\PwaBundle\\ServiceWorkerRule\\BackgroundFetchCache\:\:__construct$#'
identifier: shipmonk.deadMethod
count: 1
path: src/ServiceWorkerRule/BackgroundFetchCache.php

-
message: '#^Unused SpomkyLabs\\PwaBundle\\ServiceWorkerRule\\BackgroundFetchCache\:\:getPriority$#'
identifier: shipmonk.deadMethod
count: 1
path: src/ServiceWorkerRule/BackgroundFetchCache.php

-
message: '#^Unused SpomkyLabs\\PwaBundle\\ServiceWorkerRule\\ClearCache\:\:__construct$#'
identifier: shipmonk.deadMethod
Expand Down
27 changes: 27 additions & 0 deletions src/Dto/BackgroundFetch.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace SpomkyLabs\PwaBundle\Dto;

use Symfony\Component\Serializer\Attribute\SerializedName;

final class BackgroundFetch
{
public bool $enabled;

#[SerializedName('cache_name')]
public string $cacheName;

#[SerializedName('progress_url')]
public null|Url $progressUrl = null;

#[SerializedName('success_url')]
public null|Url $successUrl = null;

#[SerializedName('success_message')]
public null|string $successMessage = null;

#[SerializedName('failure_message')]
public null|string $failureMessage = null;
}
3 changes: 3 additions & 0 deletions src/Dto/ServiceWorker.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,7 @@ final class ServiceWorker
public bool $skipWaiting = false;

public Workbox $workbox;

#[SerializedName('background_fetch')]
public null|BackgroundFetch $backgroundFetch = null;
}
22 changes: 22 additions & 0 deletions src/Resources/config/definition/service_worker.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,28 @@
->defaultTrue()
->info('Whether the service worker should use the cache.')
->end()
->arrayNode('background_fetch')
->canBeEnabled()
->children()
->scalarNode('cache_name')
->isRequired()
->info('The name of the cache where files should be saved.')
->example(['downloads'])
->end()
->append(getUrlNode('progress_url', 'The URL of the progress page.'))
->append(getUrlNode('success_url', 'The URL of the success page.'))
->scalarNode('success_message')
->info('The message to display on success. This message can be translated.')
->defaultNull()
->example(['The download is complete.'])
->end()
->scalarNode('failure_message')
->info('The message to display on success. This message can be translated.')
->defaultNull()
->example(['The download is complete.'])
->end()
->end()
->end()
->arrayNode('workbox')
->info('The configuration of the workbox.')
->canBeDisabled()
Expand Down
Loading

0 comments on commit 4d8f943

Please sign in to comment.