From 3a09a91dd792cd825c5edd2a4c45ea1254c3ddbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 13 Jun 2022 23:23:49 +0200 Subject: [PATCH] Actions: TypeScript MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../push_notifications/{index.js => index.ts} | 9 +-- .../{registerer.js => registerer.ts} | 65 ++++++++++--------- .../actions/push_notifications/setter.js | 34 ---------- .../actions/push_notifications/setter.ts | 28 ++++++++ ...subscriptions.js => push_subscriptions.ts} | 63 +++++++++--------- 5 files changed, 99 insertions(+), 100 deletions(-) rename app/soapbox/actions/push_notifications/{index.js => index.ts} (63%) rename app/soapbox/actions/push_notifications/{registerer.js => registerer.ts} (67%) delete mode 100644 app/soapbox/actions/push_notifications/setter.js create mode 100644 app/soapbox/actions/push_notifications/setter.ts rename app/soapbox/actions/{push_subscriptions.js => push_subscriptions.ts} (65%) diff --git a/app/soapbox/actions/push_notifications/index.js b/app/soapbox/actions/push_notifications/index.ts similarity index 63% rename from app/soapbox/actions/push_notifications/index.js rename to app/soapbox/actions/push_notifications/index.ts index 32b0ffcaf..973dca919 100644 --- a/app/soapbox/actions/push_notifications/index.js +++ b/app/soapbox/actions/push_notifications/index.ts @@ -7,6 +7,8 @@ import { setAlerts, } from './setter'; +import type { AppDispatch } from 'soapbox/store'; + export { SET_BROWSER_SUPPORT, SET_SUBSCRIPTION, @@ -15,9 +17,8 @@ export { register, }; -export function changeAlerts(path, value) { - return dispatch => { +export const changeAlerts = (path: Array, value: any) => + (dispatch: AppDispatch) => { dispatch(setAlerts(path, value)); - dispatch(saveSettings()); + dispatch(saveSettings() as any); }; -} diff --git a/app/soapbox/actions/push_notifications/registerer.js b/app/soapbox/actions/push_notifications/registerer.ts similarity index 67% rename from app/soapbox/actions/push_notifications/registerer.js rename to app/soapbox/actions/push_notifications/registerer.ts index 666be8560..8a79ffdd2 100644 --- a/app/soapbox/actions/push_notifications/registerer.js +++ b/app/soapbox/actions/push_notifications/registerer.ts @@ -1,12 +1,15 @@ -import { createPushSubsription, updatePushSubscription } from 'soapbox/actions/push_subscriptions'; +import { createPushSubscription, updatePushSubscription } from 'soapbox/actions/push_subscriptions'; import { pushNotificationsSetting } from 'soapbox/settings'; import { getVapidKey } from 'soapbox/utils/auth'; import { decode as decodeBase64 } from 'soapbox/utils/base64'; import { setBrowserSupport, setSubscription, clearSubscription } from './setter'; +import type { AppDispatch, RootState } from 'soapbox/store'; +import type { Me } from 'soapbox/types/soapbox'; + // Taken from https://www.npmjs.com/package/web-push -const urlBase64ToUint8Array = (base64String) => { +const urlBase64ToUint8Array = (base64String: string) => { const padding = '='.repeat((4 - base64String.length % 4) % 4); const base64 = (base64String + padding) .replace(/\-/g, '+') @@ -17,22 +20,25 @@ const urlBase64ToUint8Array = (base64String) => { const getRegistration = () => navigator.serviceWorker.ready; -const getPushSubscription = (registration) => +const getPushSubscription = (registration: ServiceWorkerRegistration) => registration.pushManager.getSubscription() .then(subscription => ({ registration, subscription })); -const subscribe = (registration, getState) => +const subscribe = (registration: ServiceWorkerRegistration, getState: () => RootState) => registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(getVapidKey(getState())), }); -const unsubscribe = ({ registration, subscription }) => - subscription ? subscription.unsubscribe().then(() => registration) : registration; +const unsubscribe = ({ registration, subscription }: { + registration: ServiceWorkerRegistration, + subscription: PushSubscription | null, +}) => + subscription ? subscription.unsubscribe().then(() => registration) : new Promise(r => r(registration)); -const sendSubscriptionToBackend = (subscription, me) => { - return (dispatch, getState) => { - const alerts = getState().getIn(['push_notifications', 'alerts']).toJS(); +const sendSubscriptionToBackend = (subscription: PushSubscription, me: Me) => + (dispatch: AppDispatch, getState: () => RootState) => { + const alerts = getState().push_notifications.get('alerts').toJS(); const params = { subscription, data: { alerts } }; if (me) { @@ -42,16 +48,15 @@ const sendSubscriptionToBackend = (subscription, me) => { } } - return dispatch(createPushSubsription(params)); + return dispatch(createPushSubscription(params) as any); }; -}; // Last one checks for payload support: https://web-push-book.gauntface.com/chapter-06/01-non-standards-browsers/#no-payload const supportsPushNotifications = ('serviceWorker' in navigator && 'PushManager' in window && 'getKey' in PushSubscription.prototype); -export function register() { - return (dispatch, getState) => { - const me = getState().get('me'); +export const register = () => + (dispatch: AppDispatch, getState: () => RootState) => { + const me = getState().me; const vapidKey = getVapidKey(getState()); dispatch(setBrowserSupport(supportsPushNotifications)); @@ -68,35 +73,39 @@ export function register() { getRegistration() .then(getPushSubscription) - .then(({ registration, subscription }) => { + // @ts-ignore + .then(({ registration, subscription }: { + registration: ServiceWorkerRegistration, + subscription: PushSubscription | null, + }) => { if (subscription !== null) { // We have a subscription, check if it is still valid - const currentServerKey = (new Uint8Array(subscription.options.applicationServerKey)).toString(); + const currentServerKey = (new Uint8Array(subscription.options.applicationServerKey!)).toString(); const subscriptionServerKey = urlBase64ToUint8Array(vapidKey).toString(); - const serverEndpoint = getState().getIn(['push_notifications', 'subscription', 'endpoint']); + const serverEndpoint = getState().push_notifications.getIn(['subscription', 'endpoint']); // If the VAPID public key did not change and the endpoint corresponds // to the endpoint saved in the backend, the subscription is valid if (subscriptionServerKey === currentServerKey && subscription.endpoint === serverEndpoint) { - return subscription; + return { subscription }; } else { // Something went wrong, try to subscribe again - return unsubscribe({ registration, subscription }).then(registration => { + return unsubscribe({ registration, subscription }).then((registration: ServiceWorkerRegistration) => { return subscribe(registration, getState); }).then( - subscription => dispatch(sendSubscriptionToBackend(subscription, me))); + (subscription: PushSubscription) => dispatch(sendSubscriptionToBackend(subscription, me) as any)); } } // No subscription, try to subscribe return subscribe(registration, getState) - .then(subscription => dispatch(sendSubscriptionToBackend(subscription, me))); + .then(subscription => dispatch(sendSubscriptionToBackend(subscription, me) as any)); }) - .then(subscription => { + .then(({ subscription }: { subscription: PushSubscription | Record }) => { // If we got a PushSubscription (and not a subscription object from the backend) // it means that the backend subscription is valid (and was set during hydration) if (!(subscription instanceof PushSubscription)) { - dispatch(setSubscription(subscription)); + dispatch(setSubscription(subscription as PushSubscription)); if (me) { pushNotificationsSetting.set(me, { alerts: subscription.alerts }); } @@ -123,14 +132,13 @@ export function register() { }) .catch(console.warn); }; -} -export function saveSettings() { - return (dispatch, getState) => { - const state = getState().get('push_notifications'); +export const saveSettings = () => + (dispatch: AppDispatch, getState: () => RootState) => { + const state = getState().push_notifications; const alerts = state.get('alerts'); const data = { alerts }; - const me = getState().get('me'); + const me = getState().me; return dispatch(updatePushSubscription({ data })).then(() => { if (me) { @@ -138,4 +146,3 @@ export function saveSettings() { } }).catch(console.warn); }; -} diff --git a/app/soapbox/actions/push_notifications/setter.js b/app/soapbox/actions/push_notifications/setter.js deleted file mode 100644 index d77f1e4cd..000000000 --- a/app/soapbox/actions/push_notifications/setter.js +++ /dev/null @@ -1,34 +0,0 @@ -export const SET_BROWSER_SUPPORT = 'PUSH_NOTIFICATIONS_SET_BROWSER_SUPPORT'; -export const SET_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_SET_SUBSCRIPTION'; -export const CLEAR_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_CLEAR_SUBSCRIPTION'; -export const SET_ALERTS = 'PUSH_NOTIFICATIONS_SET_ALERTS'; - -export function setBrowserSupport(value) { - return { - type: SET_BROWSER_SUPPORT, - value, - }; -} - -export function setSubscription(subscription) { - return { - type: SET_SUBSCRIPTION, - subscription, - }; -} - -export function clearSubscription() { - return { - type: CLEAR_SUBSCRIPTION, - }; -} - -export function setAlerts(path, value) { - return dispatch => { - dispatch({ - type: SET_ALERTS, - path, - value, - }); - }; -} diff --git a/app/soapbox/actions/push_notifications/setter.ts b/app/soapbox/actions/push_notifications/setter.ts new file mode 100644 index 000000000..a46bd4427 --- /dev/null +++ b/app/soapbox/actions/push_notifications/setter.ts @@ -0,0 +1,28 @@ +import type { AnyAction } from 'redux'; + +export const SET_BROWSER_SUPPORT = 'PUSH_NOTIFICATIONS_SET_BROWSER_SUPPORT'; +export const SET_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_SET_SUBSCRIPTION'; +export const CLEAR_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_CLEAR_SUBSCRIPTION'; +export const SET_ALERTS = 'PUSH_NOTIFICATIONS_SET_ALERTS'; + +export const setBrowserSupport = (value: boolean) => ({ + type: SET_BROWSER_SUPPORT, + value, +}); + +export const setSubscription = (subscription: PushSubscription) => ({ + type: SET_SUBSCRIPTION, + subscription, +}); + +export const clearSubscription = () => ({ + type: CLEAR_SUBSCRIPTION, +}); + +export const setAlerts = (path: Array, value: any) => + (dispatch: React.Dispatch) => + dispatch({ + type: SET_ALERTS, + path, + value, + }); diff --git a/app/soapbox/actions/push_subscriptions.js b/app/soapbox/actions/push_subscriptions.ts similarity index 65% rename from app/soapbox/actions/push_subscriptions.js rename to app/soapbox/actions/push_subscriptions.ts index 5b47a4c93..d44b064cc 100644 --- a/app/soapbox/actions/push_subscriptions.js +++ b/app/soapbox/actions/push_subscriptions.ts @@ -16,47 +16,44 @@ export const PUSH_SUBSCRIPTION_DELETE_REQUEST = 'PUSH_SUBSCRIPTION_DELETE_REQUES export const PUSH_SUBSCRIPTION_DELETE_SUCCESS = 'PUSH_SUBSCRIPTION_DELETE_SUCCESS'; export const PUSH_SUBSCRIPTION_DELETE_FAIL = 'PUSH_SUBSCRIPTION_DELETE_FAIL'; -export function createPushSubsription(params) { - return (dispatch, getState) => { +import type { AppDispatch, RootState } from 'soapbox/store'; + +export const createPushSubscription = (params: Record) => + (dispatch: AppDispatch, getState: () => RootState) => { dispatch({ type: PUSH_SUBSCRIPTION_CREATE_REQUEST, params }); - return api(getState).post('/api/v1/push/subscription', params).then(({ data: subscription }) => { - dispatch({ type: PUSH_SUBSCRIPTION_CREATE_SUCCESS, params, subscription }); - return subscription; - }).catch(error => { - dispatch({ type: PUSH_SUBSCRIPTION_CREATE_FAIL, params, error }); - }); + return api(getState).post('/api/v1/push/subscription', params).then(({ data: subscription }) => + dispatch({ type: PUSH_SUBSCRIPTION_CREATE_SUCCESS, params, subscription }), + ).catch(error => + dispatch({ type: PUSH_SUBSCRIPTION_CREATE_FAIL, params, error }), + ); }; -} -export function fetchPushSubsription() { - return (dispatch, getState) => { +export const fetchPushSubscription = () => + (dispatch: AppDispatch, getState: () => RootState) => { dispatch({ type: PUSH_SUBSCRIPTION_FETCH_REQUEST }); - return api(getState).get('/api/v1/push/subscription').then(({ data: subscription }) => { - dispatch({ type: PUSH_SUBSCRIPTION_FETCH_SUCCESS, subscription }); - }).catch(error => { - dispatch({ type: PUSH_SUBSCRIPTION_FETCH_FAIL, error }); - }); + return api(getState).get('/api/v1/push/subscription').then(({ data: subscription }) => + dispatch({ type: PUSH_SUBSCRIPTION_FETCH_SUCCESS, subscription }), + ).catch(error => + dispatch({ type: PUSH_SUBSCRIPTION_FETCH_FAIL, error }), + ); }; -} -export function updatePushSubscription(params) { - return (dispatch, getState) => { +export const updatePushSubscription = (params: Record) => + (dispatch: AppDispatch, getState: () => any) => { dispatch({ type: PUSH_SUBSCRIPTION_UPDATE_REQUEST, params }); - return api(getState).put('/api/v1/push/subscription', params).then(({ data: subscription }) => { - dispatch({ type: PUSH_SUBSCRIPTION_UPDATE_SUCCESS, params, subscription }); - }).catch(error => { - dispatch({ type: PUSH_SUBSCRIPTION_UPDATE_FAIL, params, error }); - }); + return api(getState).put('/api/v1/push/subscription', params).then(({ data: subscription }) => + dispatch({ type: PUSH_SUBSCRIPTION_UPDATE_SUCCESS, params, subscription }), + ).catch(error => + dispatch({ type: PUSH_SUBSCRIPTION_UPDATE_FAIL, params, error }), + ); }; -} -export function deletePushSubsription() { - return (dispatch, getState) => { +export const deletePushSubscription = () => + (dispatch: AppDispatch, getState: () => RootState) => { dispatch({ type: PUSH_SUBSCRIPTION_DELETE_REQUEST }); - return api(getState).delete('/api/v1/push/subscription').then(() => { - dispatch({ type: PUSH_SUBSCRIPTION_DELETE_SUCCESS }); - }).catch(error => { - dispatch({ type: PUSH_SUBSCRIPTION_DELETE_FAIL, error }); - }); + return api(getState).delete('/api/v1/push/subscription').then(() => + dispatch({ type: PUSH_SUBSCRIPTION_DELETE_SUCCESS }), + ).catch(error => + dispatch({ type: PUSH_SUBSCRIPTION_DELETE_FAIL, error }), + ); }; -}