704452732a
* Add hints if an error occurs subscribing to notifications * Update import (type/linting issue) * disable optimization for debugging * Revert "disable optimization for debugging" This reverts commit 5b837f94e97b7488a7dc565e7f74d399e19c286f. * improve detection of notification support + improve ux / ui surrounding that * update translations
118 lines
3.7 KiB
JavaScript
118 lines
3.7 KiB
JavaScript
// @flow
|
|
/*
|
|
* This module is responsible for managing browser push notification
|
|
* subscriptions via the firebase SDK.
|
|
*/
|
|
|
|
import { Lbryio } from 'lbryinc';
|
|
import { initializeApp } from 'firebase/app';
|
|
import { getMessaging, getToken, deleteToken } from 'firebase/messaging';
|
|
import { firebaseConfig, vapidKey } from '$web/src/firebase-config';
|
|
import { addRegistration, removeRegistration, hasRegistration } from '$web/src/push-notifications/fcm-management';
|
|
import { browserData } from '$web/src/ua';
|
|
import { isPushSupported } from '$web/src/push-notifications/push-supported';
|
|
|
|
let messaging = null;
|
|
let pushSystem = null;
|
|
|
|
(async () => {
|
|
const supported = await isPushSupported();
|
|
if (supported) {
|
|
const app = initializeApp(firebaseConfig);
|
|
messaging = getMessaging(app);
|
|
pushSystem = {
|
|
supported: true,
|
|
subscribe,
|
|
unsubscribe,
|
|
subscribed,
|
|
reconnect,
|
|
disconnect,
|
|
validate,
|
|
};
|
|
}
|
|
})();
|
|
|
|
// Proxy will forward to push system if it's supported.
|
|
// $FlowIssue[incompatible-type]
|
|
export default new Proxy(
|
|
{},
|
|
{
|
|
get(target, prop) {
|
|
if (pushSystem) {
|
|
return pushSystem[prop];
|
|
} else {
|
|
if (prop === 'supported') return false;
|
|
throw new Error('Push notifications are not supported in this browser environment.');
|
|
}
|
|
},
|
|
}
|
|
);
|
|
|
|
const subscriptionMetaData = () => {
|
|
const isMobile = window.navigator.userAgentData?.mobile || false;
|
|
const browserName = browserData.browser?.name || 'unknown';
|
|
const osName = browserData.os?.name || 'unknown';
|
|
return { type: `web-${isMobile ? 'mobile' : 'desktop'}`, name: `${browserName}-${osName}` };
|
|
};
|
|
|
|
const getFcmToken = async (): Promise<string | void> => {
|
|
const swRegistration = await navigator.serviceWorker?.ready;
|
|
if (!swRegistration) return;
|
|
return getToken(messaging, { serviceWorkerRegistration: swRegistration, vapidKey });
|
|
};
|
|
|
|
const subscribe = async (userId: number, permanent: boolean = true): Promise<boolean> => {
|
|
try {
|
|
const fcmToken = await getFcmToken();
|
|
if (!fcmToken) return false;
|
|
await Lbryio.call('cfm', 'add', { token: fcmToken, ...subscriptionMetaData() });
|
|
if (permanent) addRegistration(userId);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
const unsubscribe = async (userId: number, permanent: boolean = true): Promise<boolean> => {
|
|
try {
|
|
const fcmToken = await getFcmToken();
|
|
if (!fcmToken) return false;
|
|
await deleteToken(messaging);
|
|
await Lbryio.call('cfm', 'remove', { token: fcmToken });
|
|
if (permanent) removeRegistration(userId);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
const subscribed = async (userId: number): Promise<boolean> => {
|
|
const swRegistration = await navigator.serviceWorker?.ready;
|
|
if (!swRegistration || !swRegistration.pushManager) return false;
|
|
const browserSubscriptionExists = (await swRegistration.pushManager.getSubscription()) !== null;
|
|
const userRecordExists = hasRegistration(userId);
|
|
return browserSubscriptionExists && userRecordExists;
|
|
};
|
|
|
|
const reconnect = async (userId: number): Promise<boolean> => {
|
|
if (hasRegistration(userId)) return subscribe(userId, false);
|
|
return false;
|
|
};
|
|
|
|
const disconnect = async (userId: number): Promise<boolean> => {
|
|
if (hasRegistration(userId)) return unsubscribe(userId, false);
|
|
return false;
|
|
};
|
|
|
|
const validate = async (userId: number) => {
|
|
if (!hasRegistration(userId)) return;
|
|
window.requestIdleCallback(async () => {
|
|
const serverTokens = await Lbryio.call('cfm', 'list');
|
|
const fcmToken = await getFcmToken();
|
|
if (!fcmToken) return;
|
|
const exists = serverTokens.find((item) => item.value === fcmToken);
|
|
if (!exists) {
|
|
await subscribe(userId, false);
|
|
}
|
|
});
|
|
};
|