@selfcommunity/react-core
Version:
React Core Components useful for integrating UI Community components (react-ui).
366 lines (365 loc) • 18 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.SUPPORTED_BROWSER_TYPES = exports.DEFAULT_BROWSER_TYPE = exports.EDGE_BROWSER_TYPE = exports.OPERA_BROWSER_TYPE = exports.FIREFOX_BROWSER_TYPE = exports.CHROME_BROWSER_TYPE = void 0;
const tslib_1 = require("tslib");
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const SCContextProvider_1 = require("../components/provider/SCContextProvider");
const SCUserProvider_1 = require("../components/provider/SCUserProvider");
const utils_1 = require("@selfcommunity/utils");
const Button_1 = tslib_1.__importDefault(require("@mui/material/Button"));
const utils_2 = require("@selfcommunity/utils");
const Errors_1 = require("../constants/Errors");
const api_services_1 = require("@selfcommunity/api-services");
const Device_1 = require("../constants/Device");
const SCPreferencesProvider_1 = require("../components/provider/SCPreferencesProvider");
const notistack_1 = require("notistack");
const SCPreferences = tslib_1.__importStar(require("../constants/Preferences"));
const Notifications_1 = require("../constants/Notifications");
const js_cookie_1 = tslib_1.__importDefault(require("js-cookie"));
const react_intl_1 = require("react-intl");
const notification_1 = require("../utils/notification");
/**
* Browser supported on backend
*/
exports.CHROME_BROWSER_TYPE = 'chrome';
exports.FIREFOX_BROWSER_TYPE = 'firefox';
exports.OPERA_BROWSER_TYPE = 'opera';
exports.EDGE_BROWSER_TYPE = 'edge';
exports.DEFAULT_BROWSER_TYPE = exports.CHROME_BROWSER_TYPE;
exports.SUPPORTED_BROWSER_TYPES = [exports.CHROME_BROWSER_TYPE, exports.FIREFOX_BROWSER_TYPE, exports.OPERA_BROWSER_TYPE, exports.EDGE_BROWSER_TYPE];
/**
:::info
This custom hook is used to init web push messaging.
:::
*/
function useSCWebPushMessaging() {
// CONTEXT
const scContext = (0, SCContextProvider_1.useSCContext)();
const scUserContext = (0, SCUserProvider_1.useSCUser)();
const scPreferencesContext = (0, react_1.useContext)(SCPreferencesProvider_1.SCPreferencesContext);
const intl = (0, react_intl_1.useIntl)();
const { enqueueSnackbar, closeSnackbar } = (0, notistack_1.useSnackbar)();
// STATE
const [wpSubscription, setWpSubscription] = (0, react_1.useState)(null);
// REFS
const subAttempts = (0, react_1.useRef)(0);
// CONST
const applicationServerKey = scContext.settings.notifications.webPushMessaging.applicationServerKey !== undefined
? scContext.settings.notifications.webPushMessaging.applicationServerKey
: SCPreferences.PROVIDERS_WEB_PUSH_PUBLIC_KEY in scPreferencesContext.preferences
? scPreferencesContext.preferences[SCPreferences.PROVIDERS_WEB_PUSH_PUBLIC_KEY].value
: null;
const webPushPreferenceEnabled = SCPreferences.PROVIDERS_WEB_PUSH_ENABLED in scPreferencesContext.preferences &&
scPreferencesContext.preferences[SCPreferences.PROVIDERS_WEB_PUSH_ENABLED].value
? scPreferencesContext.preferences[SCPreferences.PROVIDERS_WEB_PUSH_ENABLED].value
: false;
/**
* Show custom Snackbar dialog to request permission
* User action required for some browser (ex. Safari)
*/
const showCustomRequestNotificationSnackbar = () => {
if (!js_cookie_1.default.get(Notifications_1.NOTIFICATIONS_WEB_PUSH_MESSAGING_DIALOG_COOKIE)) {
enqueueSnackbar(intl.formatMessage({ id: 'ui.webPushNotification.requestPermission', defaultMessage: 'ui.webPushNotification.requestPermission' }), {
action: (snackbarId) => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Button_1.default, Object.assign({ size: "small", sx: { color: '#FFF' }, onClick: () => requestNotificationPermission(snackbarId) }, { children: intl.formatMessage({ id: 'ui.webPushNotification.allow', defaultMessage: 'ui.webPushNotification.allow' }) })), (0, jsx_runtime_1.jsx)(Button_1.default, Object.assign({ size: "small", sx: { color: '#FFF' }, onClick: () => closeRequestNotificationSnackbar(snackbarId) }, { children: intl.formatMessage({ id: 'ui.webPushNotification.block', defaultMessage: 'ui.webPushNotification.block' }) }))] })),
variant: 'default',
anchorOrigin: { horizontal: 'center', vertical: 'bottom' },
preventDuplicate: true,
});
}
else {
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, 'Skip show the dialog to request Web Push Notifications grant permission');
}
};
/**
* Close the SnackBar dialog if the request notification permission
* came from the custom dialog
* @param snackbarId
*/
const closeRequestNotificationSnackbar = (snackbarId) => {
if (snackbarId) {
closeSnackbar(snackbarId);
// To set expiration in minutes:
// let inFifteenMinutes = new Date(new Date().getTime() + 15 * 60 * 1000);
// {expires: inFifteenMinutes}
js_cookie_1.default.set(Notifications_1.NOTIFICATIONS_WEB_PUSH_MESSAGING_DIALOG_COOKIE, '1', { expires: 1 });
}
};
/**
* perform update the Subscription
* @param data
* @param remove
*/
const performUpdateSubscription = (data, remove) => {
const url = remove
? api_services_1.Endpoints.DeleteDevice.url({ type: Device_1.WEB_PUSH_NOTIFICATION_DEVICE_TYPE, id: data.registration_id })
: api_services_1.Endpoints.NewDevice.url({ type: Device_1.WEB_PUSH_NOTIFICATION_DEVICE_TYPE });
const method = remove ? api_services_1.Endpoints.DeleteDevice.method : api_services_1.Endpoints.NewDevice.method;
return api_services_1.http
.request(Object.assign({ url,
method }, (remove ? {} : { data: data })))
.then((res) => {
if (res.status >= 300) {
return Promise.reject(res);
}
return Promise.resolve(res.data);
});
};
/**
* Return browser type
* Fallback to DEFAULT_BROWSER_TYPE if the browser detected not in SUPPORTED_BROWSER_TYPES
*/
const getBrowserType = (browser) => {
if (exports.SUPPORTED_BROWSER_TYPES.indexOf(browser.name) < 0) {
return exports.DEFAULT_BROWSER_TYPE;
}
return browser.name;
};
/**
* Return registrationId based on browser type and subscription
* Fallback to subscription.endpoint if the browser detected not in SUPPORTED_BROWSER_TYPES
*/
const getRegistrationId = (browser, sub) => {
if (exports.SUPPORTED_BROWSER_TYPES.indexOf(browser.name) < 0) {
return sub.endpoint;
}
try {
const endpointParts = sub.endpoint.split('/');
return endpointParts[endpointParts.length - 1];
}
catch (e) {
return sub.endpoint;
}
};
/**
* updateSubscriptionOnServer to sync current subscription
* @param sub
* @param remove
*/
const updateSubscriptionOnServer = (sub, remove) => {
const browser = (0, utils_2.loadVersionBrowser)();
const data = {
browser: getBrowserType(browser).toUpperCase(),
p256dh: btoa(String.fromCharCode.apply(null, new Uint8Array(sub.getKey('p256dh')))),
auth: btoa(String.fromCharCode.apply(null, new Uint8Array(sub.getKey('auth')))),
user: scUserContext.user ? scUserContext.user.username : '',
registration_id: getRegistrationId(browser, sub),
};
return performUpdateSubscription(data, remove)
.then((res) => {
if (remove) {
setWpSubscription(null);
}
else {
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, 'Web Push Notifications subscription created successfully');
setWpSubscription(res);
}
})
.catch((e) => {
utils_1.Logger.error(Errors_1.SCOPE_SC_CORE, e);
});
};
/**
* Unsubscribe
* @param callback
*/
const unsubscribe = (callback = null) => {
navigator.serviceWorker.ready.then(function (serviceWorkerRegistration) {
if (serviceWorkerRegistration) {
// To unsubscribe from push messaging, you need get the
// subscription object, which you can call unsubscribe() on.
serviceWorkerRegistration.pushManager
.getSubscription()
.then((pushSubscription) => {
// Check we have a subscription to unsubscribe
if (!pushSubscription) {
// No subscription object, so set the state
// to allow the user to subscribe to push
return;
}
// We have a subscription, so call unsubscribe on it
pushSubscription
.unsubscribe()
.then(() => {
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, 'Unsubscription successfully');
// Request to server to remove
// the users data from your data store so you
// don't attempt to send them push messages anymore
void updateSubscriptionOnServer(pushSubscription, true).then(() => {
callback && callback();
});
})
.catch(function (e) {
// We failed to unsubscribe, this can lead to
// an unusual state, so may be best to remove
// the subscription id from your data store and
// inform the user that you disabled push
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, `Error on removing web push notification subscription.`);
console.log(e);
});
})
.catch(function (e) {
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, `Error thrown while unsubscribing from push messaging. ${e}`);
});
}
});
};
/**
* Get subscription
*/
const subscribe = () => {
navigator.serviceWorker.getRegistration().then(function (serviceWorkerRegistration) {
if (serviceWorkerRegistration) {
// service worker is registered
serviceWorkerRegistration.pushManager
.subscribe({ userVisibleOnly: true, applicationServerKey: (0, utils_2.urlB64ToUint8Array)(applicationServerKey) })
.then(function (subscription) {
if (!subscription) {
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, 'We aren’t subscribed to push.');
}
else {
void updateSubscriptionOnServer(subscription, false);
}
})
.catch(function (e) {
console.log(e);
if (Notification.permission === 'denied') {
// The user denied the notification permission which
// means we failed to subscribe and the user will need
// to manually change the notification permission to
// subscribe to push messages
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, 'Permission for Web Push Notifications was denied');
}
else {
// A problem occurred with the subscription
subAttempts.current += 1;
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, `Unable to subscribe(${subAttempts.current}) to push. ${e}`);
if (subAttempts.current < 3) {
unsubscribe(() => subscribe());
}
else {
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, `Unable to subscribe to Web Push Notification. ${e}`);
}
}
});
}
else {
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, `To receive Web Push Notifications the service worker must be registered.`);
}
});
};
/**
* Request web push notification permission
* @type {(function(): void)|*}
*/
const requestNotificationPermission = (snackbarId) => {
if (Notification.permission !== 'granted') {
Notification.requestPermission()
.then(function (permission) {
if (permission === 'granted') {
navigator.serviceWorker.ready.then((serviceWorkerRegistration) => {
// Do we already have a push message subscription?
serviceWorkerRegistration.pushManager
.getSubscription()
.then(function (subscription) {
// Enable any UI which subscribes / unsubscribes from
// push messages.
if (!subscription) {
subscribe();
return;
}
// Keep your server in sync with the latest subscription
void updateSubscriptionOnServer(subscription, false);
})
.catch((err) => {
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, 'Error during getSubscription()');
console.log(err);
});
});
}
else {
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, 'Permission for Notifications was denied');
}
})
.catch((err) => {
console.log(err);
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, 'Request web push permission by a user generated gesture');
});
}
closeRequestNotificationSnackbar(snackbarId);
};
/**
* Initialize state
*/
const initialiseState = () => {
/**
* Check if Notifications supported in the service worker
*/
if (typeof ServiceWorkerRegistration === 'undefined') {
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, "Notifications aren't supported.");
return;
}
/**
* Features, such as service workers and push notifications, require HTTPS secure domains,
* so your development environment must serve content over HTTPS to match a production environment.
* Check if push messaging is supported.
*/
if (!('PushManager' in window && 'serviceWorker' in navigator && 'Notification' in window)) {
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, "Service Worker or Push messaging aren't supported.");
return;
}
/**
* Check the current Notification permission.
* If it's denied, it's a permanent block until the user changes the permission
*/
if (Notification.permission === 'denied') {
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, 'The user has blocked Web Push Notifications.');
return;
}
else {
if (Notification.permission === 'default') {
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, 'Request permission');
if (navigator.userAgent.includes('Safari') && !navigator.userAgent.includes('Chrome')) {
showCustomRequestNotificationSnackbar();
}
else {
requestNotificationPermission(null);
}
}
else if (Notification.permission === 'granted') {
subscribe();
}
}
};
/**
* Init state web push subscription
* If web push enabled, applicationServerKey and user is logged, check subscription
*/
(0, react_1.useEffect)(() => {
if (!wpSubscription && scUserContext.user) {
if (!(0, notification_1.isWebPushMessagingEnabled)()) {
utils_1.Logger.warn(Errors_1.SCOPE_SC_CORE, 'Mobile native notifications replace web push messages with this settings.');
}
else if (!webPushPreferenceEnabled || scContext.settings.notifications.webPushMessaging.disableToastMessage) {
utils_1.Logger.warn(Errors_1.SCOPE_SC_CORE, 'This instance is not configured to support web push notifications or they have been disabled.');
}
else if (!applicationServerKey) {
utils_1.Logger.warn(Errors_1.SCOPE_SC_CORE, 'Invalid or missing applicationServerKey. Check the configuration that is passed to the SCContextProvider.');
}
else {
utils_1.Logger.info(Errors_1.SCOPE_SC_CORE, 'Initialize web push notification.');
initialiseState();
}
}
if ((!scUserContext.user || !(0, notification_1.isWebPushMessagingEnabled)()) && wpSubscription) {
// Unsubscribe if user not in session
unsubscribe(() => {
if (navigator.userAgent.includes('Safari') && !navigator.userAgent.includes('Chrome')) {
js_cookie_1.default.remove(Notifications_1.NOTIFICATIONS_WEB_PUSH_MESSAGING_DIALOG_COOKIE);
}
});
}
}, [scUserContext.user, scContext.settings.notifications.webPushMessaging, wpSubscription]);
return { wpSubscription, requestNotificationPermission };
}
exports.default = useSCWebPushMessaging;
;