@ -7,23 +7,9 @@
|
||||
*/
|
||||
|
||||
import {isPlatformBrowser} from '@angular/common';
|
||||
import {Inject, Injectable, PLATFORM_ID} from '@angular/core';
|
||||
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {ConnectableObservable} from 'rxjs/observable/ConnectableObservable';
|
||||
import {concat as obs_concat} from 'rxjs/observable/concat';
|
||||
import {defer as obs_defer} from 'rxjs/observable/defer';
|
||||
import {fromEvent as obs_fromEvent} from 'rxjs/observable/fromEvent';
|
||||
import {of as obs_of} from 'rxjs/observable/of';
|
||||
import {_throw as obs_throw} from 'rxjs/observable/throw';
|
||||
import {_do as op_do} from 'rxjs/operator/do';
|
||||
import {filter as op_filter} from 'rxjs/operator/filter';
|
||||
import {map as op_map} from 'rxjs/operator/map';
|
||||
import {publish as op_publish} from 'rxjs/operator/publish';
|
||||
import {startWith as op_startWith} from 'rxjs/operator/startWith';
|
||||
import {switchMap as op_switchMap} from 'rxjs/operator/switchMap';
|
||||
import {take as op_take} from 'rxjs/operator/take';
|
||||
import {toPromise as op_toPromise} from 'rxjs/operator/toPromise';
|
||||
import {Inject, PLATFORM_ID} from '@angular/core';
|
||||
import {ConnectableObservable, Observable, concat, defer, fromEvent, of , throwError} from 'rxjs';
|
||||
import {filter, map, publish, switchMap, take, tap} from 'rxjs/operators';
|
||||
|
||||
export const ERR_SW_NOT_SUPPORTED = 'Service workers are disabled or not supported by this browser';
|
||||
|
||||
@ -52,9 +38,7 @@ export interface UpdateActivatedEvent {
|
||||
|
||||
export type IncomingEvent = UpdateAvailableEvent | UpdateActivatedEvent;
|
||||
|
||||
interface TypedEvent {
|
||||
type: string;
|
||||
}
|
||||
export interface TypedEvent { type: string; }
|
||||
|
||||
interface StatusEvent {
|
||||
type: 'STATUS';
|
||||
@ -65,7 +49,7 @@ interface StatusEvent {
|
||||
|
||||
|
||||
function errorObservable(message: string): Observable<any> {
|
||||
return obs_defer(() => obs_throw(new Error(message)));
|
||||
return defer(() => throwError(new Error(message)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,7 +69,7 @@ export class NgswCommChannel {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
readonly events: Observable<IncomingEvent>;
|
||||
readonly events: Observable<TypedEvent>;
|
||||
|
||||
constructor(
|
||||
private serviceWorker: ServiceWorkerContainer|undefined,
|
||||
@ -95,28 +79,27 @@ export class NgswCommChannel {
|
||||
this.worker = this.events = this.registration = errorObservable(ERR_SW_NOT_SUPPORTED);
|
||||
} else {
|
||||
const controllerChangeEvents =
|
||||
<Observable<any>>(obs_fromEvent(serviceWorker, 'controllerchange'));
|
||||
<Observable<any>>(fromEvent(serviceWorker, 'controllerchange'));
|
||||
const controllerChanges = <Observable<ServiceWorker|null>>(
|
||||
op_map.call(controllerChangeEvents, () => serviceWorker.controller));
|
||||
controllerChangeEvents.pipe(map(() => serviceWorker.controller)));
|
||||
|
||||
const currentController =
|
||||
<Observable<ServiceWorker|null>>(obs_defer(() => obs_of(serviceWorker.controller)));
|
||||
<Observable<ServiceWorker|null>>(defer(() => of (serviceWorker.controller)));
|
||||
|
||||
const controllerWithChanges =
|
||||
<Observable<ServiceWorker|null>>(obs_concat(currentController, controllerChanges));
|
||||
<Observable<ServiceWorker|null>>(concat(currentController, controllerChanges));
|
||||
this.worker = <Observable<ServiceWorker>>(
|
||||
op_filter.call(controllerWithChanges, (c: ServiceWorker) => !!c));
|
||||
controllerWithChanges.pipe(filter((c: ServiceWorker) => !!c)));
|
||||
|
||||
this.registration = <Observable<ServiceWorkerRegistration>>(
|
||||
op_switchMap.call(this.worker, () => serviceWorker.getRegistration()));
|
||||
this.worker.pipe(switchMap(() => serviceWorker.getRegistration())));
|
||||
|
||||
const rawEvents = obs_fromEvent(serviceWorker, 'message');
|
||||
const rawEvents = fromEvent(serviceWorker, 'message');
|
||||
|
||||
const rawEventPayload =
|
||||
<Observable<Object>>(op_map.call(rawEvents, (event: MessageEvent) => event.data));
|
||||
const eventsUnconnected = <Observable<IncomingEvent>>(
|
||||
op_filter.call(rawEventPayload, (event: Object) => !!event && !!(event as any)['type']));
|
||||
const events = <ConnectableObservable<IncomingEvent>>(op_publish.call(eventsUnconnected));
|
||||
const rawEventPayload = rawEvents.pipe(map((event: MessageEvent) => event.data));
|
||||
const eventsUnconnected =
|
||||
(rawEventPayload.pipe(filter((event: Object) => !!event && !!(event as any)['type'])));
|
||||
const events = eventsUnconnected.pipe(publish()) as ConnectableObservable<IncomingEvent>;
|
||||
this.events = events;
|
||||
events.connect();
|
||||
}
|
||||
@ -126,13 +109,14 @@ export class NgswCommChannel {
|
||||
* @internal
|
||||
*/
|
||||
postMessage(action: string, payload: Object): Promise<void> {
|
||||
const worker = op_take.call(this.worker, 1);
|
||||
const sideEffect = op_do.call(worker, (sw: ServiceWorker) => {
|
||||
sw.postMessage({
|
||||
action, ...payload,
|
||||
});
|
||||
});
|
||||
return <Promise<void>>(op_toPromise.call(sideEffect).then(() => undefined));
|
||||
return this.worker
|
||||
.pipe(take(1), tap((sw: ServiceWorker) => {
|
||||
sw.postMessage({
|
||||
action, ...payload,
|
||||
});
|
||||
}))
|
||||
.toPromise()
|
||||
.then(() => undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,34 +136,36 @@ export class NgswCommChannel {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
eventsOfType<T>(type: string): Observable<T> {
|
||||
return <Observable<T>>(
|
||||
op_filter.call(this.events, (event: T & TypedEvent) => { return event.type === type; }));
|
||||
// TODO(i): the typings and casts in this method are wonky, we should revisit it and make the
|
||||
// types flow correctly
|
||||
eventsOfType<T extends TypedEvent>(type: string): Observable<T> {
|
||||
return <Observable<T>>this.events.pipe(filter((event) => { return event.type === type; }));
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
nextEventOfType<T>(type: string): Observable<T> {
|
||||
return <Observable<T>>(op_take.call(this.eventsOfType(type), 1));
|
||||
// TODO(i): the typings and casts in this method are wonky, we should revisit it and make the
|
||||
// types flow correctly
|
||||
nextEventOfType<T extends TypedEvent>(type: string): Observable<T> {
|
||||
return <Observable<T>>(this.eventsOfType(type).pipe(take(1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
waitForStatus(nonce: number): Promise<void> {
|
||||
const statusEventsWithNonce = <Observable<StatusEvent>>(
|
||||
op_filter.call(this.eventsOfType('STATUS'), (event: StatusEvent) => event.nonce === nonce));
|
||||
const singleStatusEvent = <Observable<StatusEvent>>(op_take.call(statusEventsWithNonce, 1));
|
||||
const mapErrorAndValue =
|
||||
<Observable<void>>(op_map.call(singleStatusEvent, (event: StatusEvent) => {
|
||||
if (event.status) {
|
||||
return undefined;
|
||||
}
|
||||
throw new Error(event.error !);
|
||||
}));
|
||||
return op_toPromise.call(mapErrorAndValue);
|
||||
return this.eventsOfType<StatusEvent>('STATUS')
|
||||
.pipe(
|
||||
filter((event: StatusEvent) => event.nonce === nonce), take(1),
|
||||
map((event: StatusEvent) => {
|
||||
if (event.status) {
|
||||
return undefined;
|
||||
}
|
||||
throw new Error(event.error !);
|
||||
}))
|
||||
.toPromise();
|
||||
}
|
||||
|
||||
get isEnabled(): boolean { return !!this.serviceWorker; }
|
||||
}
|
||||
}
|
||||
|
@ -7,11 +7,9 @@
|
||||
*/
|
||||
|
||||
import {isPlatformBrowser} from '@angular/common';
|
||||
import {APP_INITIALIZER, ApplicationRef, Inject, InjectionToken, Injector, ModuleWithProviders, NgModule, PLATFORM_ID} from '@angular/core';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {filter as op_filter} from 'rxjs/operator/filter';
|
||||
import {take as op_take} from 'rxjs/operator/take';
|
||||
import {toPromise as op_toPromise} from 'rxjs/operator/toPromise';
|
||||
import {APP_INITIALIZER, ApplicationRef, InjectionToken, Injector, ModuleWithProviders, NgModule, PLATFORM_ID} from '@angular/core';
|
||||
import {Observable} from 'rxjs';
|
||||
import {filter, take} from 'rxjs/operators';
|
||||
|
||||
import {NgswCommChannel} from './low_level';
|
||||
import {SwPush} from './push';
|
||||
@ -33,10 +31,8 @@ export function ngswAppInitializer(
|
||||
options.enabled !== false)) {
|
||||
return;
|
||||
}
|
||||
const onStable =
|
||||
op_filter.call(app.isStable, (stable: boolean) => !!stable) as Observable<boolean>;
|
||||
const isStable = op_take.call(onStable, 1) as Observable<boolean>;
|
||||
const whenStable = op_toPromise.call(isStable) as Promise<boolean>;
|
||||
const whenStable =
|
||||
app.isStable.pipe(filter((stable: boolean) => !!stable), take(1)).toPromise();
|
||||
|
||||
// Wait for service worker controller changes, and fire an INITIALIZE action when a new SW
|
||||
// becomes active. This allows the SW to initialize itself even if there is no application
|
||||
|
@ -7,18 +7,13 @@
|
||||
*/
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
import {merge as obs_merge} from 'rxjs/observable/merge';
|
||||
import {never as obs_never} from 'rxjs/observable/never';
|
||||
import {map as op_map} from 'rxjs/operator/map';
|
||||
import {switchMap as op_switchMap} from 'rxjs/operator/switchMap';
|
||||
import {take as op_take} from 'rxjs/operator/take';
|
||||
import {toPromise as op_toPromise} from 'rxjs/operator/toPromise';
|
||||
import {NEVER, Observable, Subject, merge} from 'rxjs';
|
||||
import {map, switchMap, take} from 'rxjs/operators';
|
||||
|
||||
import {ERR_SW_NOT_SUPPORTED, NgswCommChannel} from './low_level';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Subscribe and listen to push notifications from the Service Worker.
|
||||
*
|
||||
@ -35,20 +30,18 @@ export class SwPush {
|
||||
|
||||
constructor(private sw: NgswCommChannel) {
|
||||
if (!sw.isEnabled) {
|
||||
this.messages = obs_never();
|
||||
this.subscription = obs_never();
|
||||
this.messages = NEVER;
|
||||
this.subscription = NEVER;
|
||||
return;
|
||||
}
|
||||
this.messages =
|
||||
op_map.call(this.sw.eventsOfType('PUSH'), (message: {data: object}) => message.data);
|
||||
this.messages = this.sw.eventsOfType('PUSH').pipe(map((message: any) => message.data));
|
||||
|
||||
this.pushManager = <Observable<PushManager>>(op_map.call(
|
||||
this.sw.registration,
|
||||
(registration: ServiceWorkerRegistration) => { return registration.pushManager; }));
|
||||
this.pushManager = this.sw.registration.pipe(
|
||||
map((registration: ServiceWorkerRegistration) => { return registration.pushManager; }));
|
||||
|
||||
const workerDrivenSubscriptions = <Observable<PushSubscription|null>>(op_switchMap.call(
|
||||
this.pushManager, (pm: PushManager) => pm.getSubscription().then(sub => { return sub; })));
|
||||
this.subscription = obs_merge(workerDrivenSubscriptions, this.subscriptionChanges);
|
||||
const workerDrivenSubscriptions = this.pushManager.pipe(
|
||||
switchMap((pm: PushManager) => pm.getSubscription().then(sub => { return sub; })));
|
||||
this.subscription = merge(workerDrivenSubscriptions, this.subscriptionChanges);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,20 +61,20 @@ export class SwPush {
|
||||
applicationServerKey[i] = key.charCodeAt(i);
|
||||
}
|
||||
pushOptions.applicationServerKey = applicationServerKey;
|
||||
const subscribe = <Observable<PushSubscription>>(
|
||||
op_switchMap.call(this.pushManager, (pm: PushManager) => pm.subscribe(pushOptions)));
|
||||
const subscribeOnce = op_take.call(subscribe, 1);
|
||||
return (op_toPromise.call(subscribeOnce) as Promise<PushSubscription>).then(sub => {
|
||||
this.subscriptionChanges.next(sub);
|
||||
return sub;
|
||||
});
|
||||
|
||||
return this.pushManager.pipe(switchMap((pm: PushManager) => pm.subscribe(pushOptions)), take(1))
|
||||
.toPromise()
|
||||
.then(sub => {
|
||||
this.subscriptionChanges.next(sub);
|
||||
return sub;
|
||||
});
|
||||
}
|
||||
|
||||
unsubscribe(): Promise<void> {
|
||||
if (!this.sw.isEnabled) {
|
||||
return Promise.reject(new Error(ERR_SW_NOT_SUPPORTED));
|
||||
}
|
||||
const unsubscribe = op_switchMap.call(this.subscription, (sub: PushSubscription | null) => {
|
||||
const unsubscribe = this.subscription.pipe(switchMap((sub: PushSubscription | null) => {
|
||||
if (sub !== null) {
|
||||
return sub.unsubscribe().then(success => {
|
||||
if (success) {
|
||||
@ -94,8 +87,7 @@ export class SwPush {
|
||||
} else {
|
||||
throw new Error('Not subscribed to push notifications.');
|
||||
}
|
||||
});
|
||||
const unsubscribeOnce = op_take.call(unsubscribe, 1);
|
||||
return op_toPromise.call(unsubscribeOnce) as Promise<void>;
|
||||
}));
|
||||
return unsubscribe.pipe(take(1)).toPromise();
|
||||
}
|
||||
}
|
||||
|
@ -7,14 +7,12 @@
|
||||
*/
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {defer as obs_defer} from 'rxjs/observable/defer';
|
||||
import {never as obs_never} from 'rxjs/observable/never';
|
||||
import {map as op_map} from 'rxjs/operator/map';
|
||||
import {NEVER, Observable} from 'rxjs';
|
||||
|
||||
import {ERR_SW_NOT_SUPPORTED, NgswCommChannel, UpdateActivatedEvent, UpdateAvailableEvent} from './low_level';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Subscribe to update notifications from the Service Worker, trigger update
|
||||
* checks, and forcibly activate updates.
|
||||
@ -28,12 +26,12 @@ export class SwUpdate {
|
||||
|
||||
constructor(private sw: NgswCommChannel) {
|
||||
if (!sw.isEnabled) {
|
||||
this.available = obs_never();
|
||||
this.activated = obs_never();
|
||||
this.available = NEVER;
|
||||
this.activated = NEVER;
|
||||
return;
|
||||
}
|
||||
this.available = this.sw.eventsOfType('UPDATE_AVAILABLE');
|
||||
this.activated = this.sw.eventsOfType('UPDATE_ACTIVATED');
|
||||
this.available = this.sw.eventsOfType<UpdateAvailableEvent>('UPDATE_AVAILABLE');
|
||||
this.activated = this.sw.eventsOfType<UpdateActivatedEvent>('UPDATE_ACTIVATED');
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user