UNPKG

@intuitionrobotics/push-pub-sub

Version:
175 lines 8.05 kB
/* * A typescript & react boilerplate with api call example * * Copyright (C) 2018 Intuition Robotics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { __stringify, BadImplementationException, compare, generateHex, ImplementationMissingException, Module } from "@intuitionrobotics/ts-common"; import { StorageKey, XhrHttpModule } from "@intuitionrobotics/thunderstorm/frontend"; // noinspection TypeScriptPreferShortImport import {} from "../../index.js"; import { HttpMethod } from "@intuitionrobotics/thunderstorm"; import { FirebaseModule } from "@intuitionrobotics/firebase/app-frontend/FirebaseModule"; import { NotificationsModule } from "./NotificationModule.js"; export const Command_SwToApp = "SwToApp"; export const pushSessionIdKey = "x-push-session-id"; const pushSessionId = new StorageKey(pushSessionIdKey, false); export class PushPubSubModule_Class extends Module { subscriptions = []; firebaseToken; messaging; pushSessionId; timeout = 800; constructor() { super("PushPubSubModule"); window.name = window.name || generateHex(32); this.pushSessionId = pushSessionId.set(window.name); } init() { if (this.config?.registerOnInit === false) return; this.initApp(); } getPushSessionId() { return this.pushSessionId; } registerServiceWorker = async () => { console.log("Registering service worker..."); return await navigator.serviceWorker.register(`/${this.config.swFileName || "ts_service_worker.js"}`); }; initApp = () => { if (!this.config?.publicKeyBase64) throw new ImplementationMissingException(`Please specify the right config for the 'PushPubSubModule'`); this.runAsync("Initializing Firebase SDK and registering SW", async () => { if ("serviceWorker" in navigator) { const asyncs = [ this.registerServiceWorker(), FirebaseModule.createSession() ]; const { 0: registration, 1: app } = await Promise.all(asyncs); await registration.update(); this.messaging = await app.getMessaging(); // this.messaging.usePublicVapidKey(this.config.publicKeyBase64); // await this.messaging.useServiceWorker(registration); await this.getToken({ vapidKey: this.config.publicKeyBase64, serviceWorkerRegistration: registration }); if (navigator.serviceWorker.controller) { console.log(`This page is currently controlled by: ${navigator.serviceWorker.controller}`); } navigator.serviceWorker.oncontrollerchange = function () { console.log("This page is now controlled by:", navigator.serviceWorker.controller); }; navigator.serviceWorker.onmessage = (event) => { this.processMessageFromSw(event.data); }; } }); }; // / need to call this from the login verified getToken = async (options) => { try { this.logVerbose("Checking/Requesting permission..."); const permission = await Notification.requestPermission(); this.logVerbose(`Notification permission: ${permission}`); if (permission !== "granted") return; if (!this.messaging) throw new BadImplementationException("I literally just set this!"); this.firebaseToken = await this.messaging.getToken(options); if (!this.firebaseToken) return; this.messaging.onMessage((payload) => { if (!payload.data) return this.logInfo('No data passed to the message handler, I got this', payload); this.processMessage(payload.data); }); this.logVerbose("new token received: " + this.firebaseToken); // this.logWarning("I don't believe there is a good reason to register whenever an app starts.. before we have any information about user or app status!!") // this.logWarning("Convince me otherwise.. :)") // Race Condition in CC proved that I didnt register for push due to the getToken being async which ended after modules init // so I had subscriptions but didnt register them if (this.subscriptions.length > 0) this.register(); } catch (err) { this.logError("Unable to get token", err); } }; processMessageFromSw = (data) => { this.logInfo("Got data from SW: ", data); if (!data.command || !data.message || data.command !== Command_SwToApp) return; this.processMessage(data.message); }; processMessage = (data) => { this.logInfo("process message", data); const arr = JSON.parse(data.messages); arr.forEach(s => { if (s.persistent) NotificationsModule.addNotification(s); this.subscriptions .filter(d => d.pushKey === s.pushKey && compare(s.props, d.props)) .forEach(d => d.callBack(s.data)); }); }; subscribe = (subscription) => { this.subscribeImpl(subscription); return this.register(); }; subscribeImpl = (subscription) => { const foundSubscription = this.subscriptions.find(d => d.pushKey === subscription.pushKey && compare(subscription.props, d.props)); if (foundSubscription) { this.logError("Subscription already exists", subscription); return; } this.subscriptions.push(subscription); }; subscribeMulti = (subscriptions) => { subscriptions.forEach(subscription => this.subscribeImpl(subscription)); return this.register(); }; unsubscribe = (subscription) => { const idx = this.subscriptions.findIndex(d => d.pushKey === subscription.pushKey && compare(subscription.props, d.props)); const foundSubscription = idx !== -1 ? this.subscriptions[idx] : undefined; if (foundSubscription) this.subscriptions.splice(idx, 1); return this.register(); }; register = () => { const firebaseToken = this.firebaseToken; if (!firebaseToken) return this.logWarning("No Firebase token..."); this.debounce(() => { const body = { firebaseToken, pushSessionId: this.getPushSessionId(), subscriptions: this.subscriptions.map(({ pushKey, props }) => ({ pushKey, props })) }; this.logDebug("Registering subscriptions"); for (const sub of this.subscriptions) { this.logDebug(`${sub.pushKey} => ${sub.props ? __stringify(sub.props) : "no props"}`); } XhrHttpModule .createRequest(HttpMethod.POST, "register-pub-sub-tab") .setRelativeUrl("/v1/push/register") .setJsonBody(body) .setOnError("Failed to register for push") .execute((response) => { NotificationsModule.setNotificationList(response); this.logVerbose("Finished register PubSub"); }); }, "debounce-register", this.timeout); }; } export const PushPubSubModule = new PushPubSubModule_Class(); //# sourceMappingURL=PushPubSubModule.js.map