|
6 | 6 | * found in the LICENSE file at https://angular.io/license |
7 | 7 | */ |
8 | 8 |
|
9 | | -import {isPlatformBrowser} from '@angular/common'; |
10 | | -import {APP_INITIALIZER, ApplicationRef, InjectionToken, Injector, ModuleWithProviders, NgModule, NgZone, PLATFORM_ID} from '@angular/core'; |
11 | | -import {merge, Observable, of} from 'rxjs'; |
12 | | -import {delay, filter, take} from 'rxjs/operators'; |
| 9 | +import {ModuleWithProviders, NgModule} from '@angular/core'; |
13 | 10 |
|
14 | | -import {NgswCommChannel} from './low_level'; |
| 11 | +import {provideServiceWorker, SwRegistrationOptions} from './provider'; |
15 | 12 | import {SwPush} from './push'; |
16 | 13 | import {SwUpdate} from './update'; |
17 | 14 |
|
18 | | -/** |
19 | | - * Token that can be used to provide options for `ServiceWorkerModule` outside of |
20 | | - * `ServiceWorkerModule.register()`. |
21 | | - * |
22 | | - * You can use this token to define a provider that generates the registration options at runtime, |
23 | | - * for example via a function call: |
24 | | - * |
25 | | - * {@example service-worker/registration-options/module.ts region="registration-options" |
26 | | - * header="app.module.ts"} |
27 | | - * |
28 | | - * @publicApi |
29 | | - */ |
30 | | -export abstract class SwRegistrationOptions { |
31 | | - /** |
32 | | - * Whether the ServiceWorker will be registered and the related services (such as `SwPush` and |
33 | | - * `SwUpdate`) will attempt to communicate and interact with it. |
34 | | - * |
35 | | - * Default: true |
36 | | - */ |
37 | | - enabled?: boolean; |
38 | | - |
39 | | - /** |
40 | | - * A URL that defines the ServiceWorker's registration scope; that is, what range of URLs it can |
41 | | - * control. It will be used when calling |
42 | | - * [ServiceWorkerContainer#register()](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register). |
43 | | - */ |
44 | | - scope?: string; |
45 | | - |
46 | | - /** |
47 | | - * Defines the ServiceWorker registration strategy, which determines when it will be registered |
48 | | - * with the browser. |
49 | | - * |
50 | | - * The default behavior of registering once the application stabilizes (i.e. as soon as there are |
51 | | - * no pending micro- and macro-tasks) is designed to register the ServiceWorker as soon as |
52 | | - * possible but without affecting the application's first time load. |
53 | | - * |
54 | | - * Still, there might be cases where you want more control over when the ServiceWorker is |
55 | | - * registered (for example, there might be a long-running timeout or polling interval, preventing |
56 | | - * the app from stabilizing). The available option are: |
57 | | - * |
58 | | - * - `registerWhenStable:<timeout>`: Register as soon as the application stabilizes (no pending |
59 | | - * micro-/macro-tasks) but no later than `<timeout>` milliseconds. If the app hasn't |
60 | | - * stabilized after `<timeout>` milliseconds (for example, due to a recurrent asynchronous |
61 | | - * task), the ServiceWorker will be registered anyway. |
62 | | - * If `<timeout>` is omitted, the ServiceWorker will only be registered once the app |
63 | | - * stabilizes. |
64 | | - * - `registerImmediately`: Register immediately. |
65 | | - * - `registerWithDelay:<timeout>`: Register with a delay of `<timeout>` milliseconds. For |
66 | | - * example, use `registerWithDelay:5000` to register the ServiceWorker after 5 seconds. If |
67 | | - * `<timeout>` is omitted, is defaults to `0`, which will register the ServiceWorker as soon |
68 | | - * as possible but still asynchronously, once all pending micro-tasks are completed. |
69 | | - * - An [Observable](guide/observables) factory function: A function that returns an `Observable`. |
70 | | - * The function will be used at runtime to obtain and subscribe to the `Observable` and the |
71 | | - * ServiceWorker will be registered as soon as the first value is emitted. |
72 | | - * |
73 | | - * Default: 'registerWhenStable:30000' |
74 | | - */ |
75 | | - registrationStrategy?: string|(() => Observable<unknown>); |
76 | | -} |
77 | | - |
78 | | -export const SCRIPT = new InjectionToken<string>('NGSW_REGISTER_SCRIPT'); |
79 | | - |
80 | | -export function ngswAppInitializer( |
81 | | - injector: Injector, script: string, options: SwRegistrationOptions, |
82 | | - platformId: string): Function { |
83 | | - return () => { |
84 | | - if (!(isPlatformBrowser(platformId) && ('serviceWorker' in navigator) && |
85 | | - options.enabled !== false)) { |
86 | | - return; |
87 | | - } |
88 | | - |
89 | | - // Wait for service worker controller changes, and fire an INITIALIZE action when a new SW |
90 | | - // becomes active. This allows the SW to initialize itself even if there is no application |
91 | | - // traffic. |
92 | | - navigator.serviceWorker.addEventListener('controllerchange', () => { |
93 | | - if (navigator.serviceWorker.controller !== null) { |
94 | | - navigator.serviceWorker.controller.postMessage({action: 'INITIALIZE'}); |
95 | | - } |
96 | | - }); |
97 | | - |
98 | | - let readyToRegister$: Observable<unknown>; |
99 | | - |
100 | | - if (typeof options.registrationStrategy === 'function') { |
101 | | - readyToRegister$ = options.registrationStrategy(); |
102 | | - } else { |
103 | | - const [strategy, ...args] = |
104 | | - (options.registrationStrategy || 'registerWhenStable:30000').split(':'); |
105 | | - |
106 | | - switch (strategy) { |
107 | | - case 'registerImmediately': |
108 | | - readyToRegister$ = of(null); |
109 | | - break; |
110 | | - case 'registerWithDelay': |
111 | | - readyToRegister$ = delayWithTimeout(+args[0] || 0); |
112 | | - break; |
113 | | - case 'registerWhenStable': |
114 | | - readyToRegister$ = !args[0] ? whenStable(injector) : |
115 | | - merge(whenStable(injector), delayWithTimeout(+args[0])); |
116 | | - break; |
117 | | - default: |
118 | | - // Unknown strategy. |
119 | | - throw new Error( |
120 | | - `Unknown ServiceWorker registration strategy: ${options.registrationStrategy}`); |
121 | | - } |
122 | | - } |
123 | | - |
124 | | - // Don't return anything to avoid blocking the application until the SW is registered. |
125 | | - // Also, run outside the Angular zone to avoid preventing the app from stabilizing (especially |
126 | | - // given that some registration strategies wait for the app to stabilize). |
127 | | - // Catch and log the error if SW registration fails to avoid uncaught rejection warning. |
128 | | - const ngZone = injector.get(NgZone); |
129 | | - ngZone.runOutsideAngular( |
130 | | - () => readyToRegister$.pipe(take(1)).subscribe( |
131 | | - () => |
132 | | - navigator.serviceWorker.register(script, {scope: options.scope}) |
133 | | - .catch(err => console.error('Service worker registration failed with:', err)))); |
134 | | - }; |
135 | | -} |
136 | | - |
137 | | -function delayWithTimeout(timeout: number): Observable<unknown> { |
138 | | - return of(null).pipe(delay(timeout)); |
139 | | -} |
140 | | - |
141 | | -function whenStable(injector: Injector): Observable<unknown> { |
142 | | - const appRef = injector.get(ApplicationRef); |
143 | | - return appRef.isStable.pipe(filter(stable => stable)); |
144 | | -} |
145 | | - |
146 | | -export function ngswCommChannelFactory( |
147 | | - opts: SwRegistrationOptions, platformId: string): NgswCommChannel { |
148 | | - return new NgswCommChannel( |
149 | | - isPlatformBrowser(platformId) && opts.enabled !== false ? navigator.serviceWorker : |
150 | | - undefined); |
151 | | -} |
152 | | - |
153 | 15 | /** |
154 | 16 | * @publicApi |
155 | 17 | */ |
156 | | -@NgModule({ |
157 | | - providers: [SwPush, SwUpdate], |
158 | | -}) |
| 18 | +@NgModule({providers: [SwPush, SwUpdate]}) |
159 | 19 | export class ServiceWorkerModule { |
160 | 20 | /** |
161 | 21 | * Register the given Angular Service Worker script. |
162 | 22 | * |
163 | 23 | * If `enabled` is set to `false` in the given options, the module will behave as if service |
164 | 24 | * workers are not supported by the browser, and the service worker will not be registered. |
165 | 25 | */ |
166 | | - static register(script: string, opts: SwRegistrationOptions = {}): |
| 26 | + static register(script: string, options: SwRegistrationOptions = {}): |
167 | 27 | ModuleWithProviders<ServiceWorkerModule> { |
168 | 28 | return { |
169 | 29 | ngModule: ServiceWorkerModule, |
170 | | - providers: [ |
171 | | - {provide: SCRIPT, useValue: script}, |
172 | | - {provide: SwRegistrationOptions, useValue: opts}, |
173 | | - { |
174 | | - provide: NgswCommChannel, |
175 | | - useFactory: ngswCommChannelFactory, |
176 | | - deps: [SwRegistrationOptions, PLATFORM_ID] |
177 | | - }, |
178 | | - { |
179 | | - provide: APP_INITIALIZER, |
180 | | - useFactory: ngswAppInitializer, |
181 | | - deps: [Injector, SCRIPT, SwRegistrationOptions, PLATFORM_ID], |
182 | | - multi: true, |
183 | | - }, |
184 | | - ], |
| 30 | + providers: [provideServiceWorker(script, options)], |
185 | 31 | }; |
186 | 32 | } |
187 | 33 | } |
0 commit comments