about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAkihiko Odaki <akihiko.odaki.4i@stu.hosei.ac.jp>2017-12-24 12:47:35 +0900
committerEugen Rochko <eugen@zeonfederated.com>2017-12-24 04:47:35 +0100
commit35fdf561be94200c7e7923ed11cde73b5b681314 (patch)
treef2c8044721025b574e192a449c56452560a05cc0
parent081956742cbcd6a51e48669fc5e3d21df0f879a7 (diff)
Refactor web_push_subscription (#6047)
* Remove onSave method in mapped properties for column_settings

* Make web_push_subscription.register an action
-rw-r--r--app/javascript/mastodon/actions/push_notifications.js57
-rw-r--r--app/javascript/mastodon/actions/push_notifications/index.js23
-rw-r--r--app/javascript/mastodon/actions/push_notifications/registerer.js149
-rw-r--r--app/javascript/mastodon/actions/push_notifications/setter.js34
-rw-r--r--app/javascript/mastodon/features/notifications/components/column_settings.js1
-rw-r--r--app/javascript/mastodon/features/notifications/containers/column_settings_container.js9
-rw-r--r--app/javascript/mastodon/main.js6
-rw-r--r--app/javascript/mastodon/reducers/push_notifications.js4
-rw-r--r--app/javascript/mastodon/web_push_subscription.js129
9 files changed, 213 insertions, 199 deletions
diff --git a/app/javascript/mastodon/actions/push_notifications.js b/app/javascript/mastodon/actions/push_notifications.js
deleted file mode 100644
index de06385f9..000000000
--- a/app/javascript/mastodon/actions/push_notifications.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import axios from 'axios';
-import { pushNotificationsSetting } from '../settings';
-
-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 ALERTS_CHANGE = 'PUSH_NOTIFICATIONS_ALERTS_CHANGE';
-
-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 changeAlerts(key, value) {
-  return dispatch => {
-    dispatch({
-      type: ALERTS_CHANGE,
-      key,
-      value,
-    });
-
-    dispatch(saveSettings());
-  };
-}
-
-export function saveSettings() {
-  return (_, getState) => {
-    const state = getState().get('push_notifications');
-    const subscription = state.get('subscription');
-    const alerts = state.get('alerts');
-    const data = { alerts };
-
-    axios.put(`/api/web/push_subscriptions/${subscription.get('id')}`, {
-      data,
-    }).then(() => {
-      const me = getState().getIn(['meta', 'me']);
-      if (me) {
-        pushNotificationsSetting.set(me, data);
-      }
-    });
-  };
-}
diff --git a/app/javascript/mastodon/actions/push_notifications/index.js b/app/javascript/mastodon/actions/push_notifications/index.js
new file mode 100644
index 000000000..376b55b62
--- /dev/null
+++ b/app/javascript/mastodon/actions/push_notifications/index.js
@@ -0,0 +1,23 @@
+import {
+  SET_BROWSER_SUPPORT,
+  SET_SUBSCRIPTION,
+  CLEAR_SUBSCRIPTION,
+  SET_ALERTS,
+  setAlerts,
+} from './setter';
+import { register, saveSettings } from './registerer';
+
+export {
+  SET_BROWSER_SUPPORT,
+  SET_SUBSCRIPTION,
+  CLEAR_SUBSCRIPTION,
+  SET_ALERTS,
+  register,
+};
+
+export function changeAlerts(key, value) {
+  return dispatch => {
+    dispatch(setAlerts(key, value));
+    dispatch(saveSettings());
+  };
+}
diff --git a/app/javascript/mastodon/actions/push_notifications/registerer.js b/app/javascript/mastodon/actions/push_notifications/registerer.js
new file mode 100644
index 000000000..f851c311c
--- /dev/null
+++ b/app/javascript/mastodon/actions/push_notifications/registerer.js
@@ -0,0 +1,149 @@
+import axios from 'axios';
+import { pushNotificationsSetting } from '../../settings';
+import { setBrowserSupport, setSubscription, clearSubscription } from './setter';
+
+// Taken from https://www.npmjs.com/package/web-push
+const urlBase64ToUint8Array = (base64String) => {
+  const padding = '='.repeat((4 - base64String.length % 4) % 4);
+  const base64 = (base64String + padding)
+    .replace(/\-/g, '+')
+    .replace(/_/g, '/');
+
+  const rawData = window.atob(base64);
+  const outputArray = new Uint8Array(rawData.length);
+
+  for (let i = 0; i < rawData.length; ++i) {
+    outputArray[i] = rawData.charCodeAt(i);
+  }
+  return outputArray;
+};
+
+const getApplicationServerKey = () => document.querySelector('[name="applicationServerKey"]').getAttribute('content');
+
+const getRegistration = () => navigator.serviceWorker.ready;
+
+const getPushSubscription = (registration) =>
+  registration.pushManager.getSubscription()
+    .then(subscription => ({ registration, subscription }));
+
+const subscribe = (registration) =>
+  registration.pushManager.subscribe({
+    userVisibleOnly: true,
+    applicationServerKey: urlBase64ToUint8Array(getApplicationServerKey()),
+  });
+
+const unsubscribe = ({ registration, subscription }) =>
+  subscription ? subscription.unsubscribe().then(() => registration) : registration;
+
+const sendSubscriptionToBackend = (subscription, me) => {
+  const params = { subscription };
+
+  if (me) {
+    const data = pushNotificationsSetting.get(me);
+    if (data) {
+      params.data = data;
+    }
+  }
+
+  return axios.post('/api/web/push_subscriptions', params).then(response => response.data);
+};
+
+// 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 default function register () {
+  return (dispatch, getState) => {
+    dispatch(setBrowserSupport(supportsPushNotifications));
+    const me = getState().getIn(['meta', 'me']);
+
+    if (me && !pushNotificationsSetting.get(me)) {
+      const alerts = getState().getIn(['push_notifications', 'alerts']);
+      if (alerts) {
+        pushNotificationsSetting.set(me, { alerts: alerts });
+      }
+    }
+
+    if (supportsPushNotifications) {
+      if (!getApplicationServerKey()) {
+        console.error('The VAPID public key is not set. You will not be able to receive Web Push Notifications.');
+        return;
+      }
+
+      getRegistration()
+        .then(getPushSubscription)
+        .then(({ registration, subscription }) => {
+          if (subscription !== null) {
+            // We have a subscription, check if it is still valid
+            const currentServerKey = (new Uint8Array(subscription.options.applicationServerKey)).toString();
+            const subscriptionServerKey = urlBase64ToUint8Array(getApplicationServerKey()).toString();
+            const serverEndpoint = getState().getIn(['push_notifications', '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;
+            } else {
+              // Something went wrong, try to subscribe again
+              return unsubscribe({ registration, subscription }).then(subscribe).then(
+                subscription => sendSubscriptionToBackend(subscription, me));
+            }
+          }
+
+          // No subscription, try to subscribe
+          return subscribe(registration).then(
+            subscription => sendSubscriptionToBackend(subscription, me));
+        })
+        .then(subscription => {
+          // 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));
+            if (me) {
+              pushNotificationsSetting.set(me, { alerts: subscription.alerts });
+            }
+          }
+        })
+        .catch(error => {
+          if (error.code === 20 && error.name === 'AbortError') {
+            console.warn('Your browser supports Web Push Notifications, but does not seem to implement the VAPID protocol.');
+          } else if (error.code === 5 && error.name === 'InvalidCharacterError') {
+            console.error('The VAPID public key seems to be invalid:', getApplicationServerKey());
+          }
+
+          // Clear alerts and hide UI settings
+          dispatch(clearSubscription());
+          if (me) {
+            pushNotificationsSetting.remove(me);
+          }
+
+          try {
+            getRegistration()
+              .then(getPushSubscription)
+              .then(unsubscribe);
+          } catch (e) {
+
+          }
+        });
+    } else {
+      console.warn('Your browser does not support Web Push Notifications.');
+    }
+  };
+}
+
+export function saveSettings() {
+  return (_, getState) => {
+    const state = getState().get('push_notifications');
+    const subscription = state.get('subscription');
+    const alerts = state.get('alerts');
+    const data = { alerts };
+
+    axios.put(`/api/web/push_subscriptions/${subscription.get('id')}`, {
+      data,
+    }).then(() => {
+      const me = getState().getIn(['meta', 'me']);
+      if (me) {
+        pushNotificationsSetting.set(me, data);
+      }
+    });
+  };
+}
diff --git a/app/javascript/mastodon/actions/push_notifications/setter.js b/app/javascript/mastodon/actions/push_notifications/setter.js
new file mode 100644
index 000000000..a2cc41c5a
--- /dev/null
+++ b/app/javascript/mastodon/actions/push_notifications/setter.js
@@ -0,0 +1,34 @@
+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 (key, value) {
+  return dispatch => {
+    dispatch({
+      type: SET_ALERTS,
+      key,
+      value,
+    });
+  };
+}
diff --git a/app/javascript/mastodon/features/notifications/components/column_settings.js b/app/javascript/mastodon/features/notifications/components/column_settings.js
index 57cded4f1..23545185c 100644
--- a/app/javascript/mastodon/features/notifications/components/column_settings.js
+++ b/app/javascript/mastodon/features/notifications/components/column_settings.js
@@ -11,7 +11,6 @@ export default class ColumnSettings extends React.PureComponent {
     settings: ImmutablePropTypes.map.isRequired,
     pushSettings: ImmutablePropTypes.map.isRequired,
     onChange: PropTypes.func.isRequired,
-    onSave: PropTypes.func.isRequired,
     onClear: PropTypes.func.isRequired,
   };
 
diff --git a/app/javascript/mastodon/features/notifications/containers/column_settings_container.js b/app/javascript/mastodon/features/notifications/containers/column_settings_container.js
index d4ead7881..f4c63fee6 100644
--- a/app/javascript/mastodon/features/notifications/containers/column_settings_container.js
+++ b/app/javascript/mastodon/features/notifications/containers/column_settings_container.js
@@ -1,9 +1,9 @@
 import { connect } from 'react-redux';
 import { defineMessages, injectIntl } from 'react-intl';
 import ColumnSettings from '../components/column_settings';
-import { changeSetting, saveSettings } from '../../../actions/settings';
+import { changeSetting } from '../../../actions/settings';
 import { clearNotifications } from '../../../actions/notifications';
-import { changeAlerts as changePushNotifications, saveSettings as savePushNotificationSettings } from '../../../actions/push_notifications';
+import { changeAlerts as changePushNotifications } from '../../../actions/push_notifications';
 import { openModal } from '../../../actions/modal';
 
 const messages = defineMessages({
@@ -26,11 +26,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
     }
   },
 
-  onSave () {
-    dispatch(saveSettings());
-    dispatch(savePushNotificationSettings());
-  },
-
   onClear () {
     dispatch(openModal('CONFIRM', {
       message: intl.formatMessage(messages.clearMessage),
diff --git a/app/javascript/mastodon/main.js b/app/javascript/mastodon/main.js
index 23b6b04fa..9b18465f5 100644
--- a/app/javascript/mastodon/main.js
+++ b/app/javascript/mastodon/main.js
@@ -1,5 +1,5 @@
-import * as WebPushSubscription from './web_push_subscription';
-import Mastodon from './containers/mastodon';
+import { register as registerPushNotifications } from './actions/push_notifications';
+import { default as Mastodon, store } from './containers/mastodon';
 import React from 'react';
 import ReactDOM from 'react-dom';
 import ready from './ready';
@@ -25,7 +25,7 @@ function main() {
     if (process.env.NODE_ENV === 'production') {
       // avoid offline in dev mode because it's harder to debug
       require('offline-plugin/runtime').install();
-      WebPushSubscription.register();
+      store.dispatch(registerPushNotifications.register());
     }
     perf.stop('main()');
   });
diff --git a/app/javascript/mastodon/reducers/push_notifications.js b/app/javascript/mastodon/reducers/push_notifications.js
index 31a40d246..c15b38fe4 100644
--- a/app/javascript/mastodon/reducers/push_notifications.js
+++ b/app/javascript/mastodon/reducers/push_notifications.js
@@ -1,5 +1,5 @@
 import { STORE_HYDRATE } from '../actions/store';
-import { SET_BROWSER_SUPPORT, SET_SUBSCRIPTION, CLEAR_SUBSCRIPTION, ALERTS_CHANGE } from '../actions/push_notifications';
+import { SET_BROWSER_SUPPORT, SET_SUBSCRIPTION, CLEAR_SUBSCRIPTION, SET_ALERTS } from '../actions/push_notifications';
 import Immutable from 'immutable';
 
 const initialState = Immutable.Map({
@@ -43,7 +43,7 @@ export default function push_subscriptions(state = initialState, action) {
     return state.set('browserSupport', action.value);
   case CLEAR_SUBSCRIPTION:
     return initialState;
-  case ALERTS_CHANGE:
+  case SET_ALERTS:
     return state.setIn(action.key, action.value);
   default:
     return state;
diff --git a/app/javascript/mastodon/web_push_subscription.js b/app/javascript/mastodon/web_push_subscription.js
deleted file mode 100644
index 17aca4060..000000000
--- a/app/javascript/mastodon/web_push_subscription.js
+++ /dev/null
@@ -1,129 +0,0 @@
-import axios from 'axios';
-import { store } from './containers/mastodon';
-import { setBrowserSupport, setSubscription, clearSubscription } from './actions/push_notifications';
-import { pushNotificationsSetting } from './settings';
-
-// Taken from https://www.npmjs.com/package/web-push
-const urlBase64ToUint8Array = (base64String) => {
-  const padding = '='.repeat((4 - base64String.length % 4) % 4);
-  const base64 = (base64String + padding)
-    .replace(/\-/g, '+')
-    .replace(/_/g, '/');
-
-  const rawData = window.atob(base64);
-  const outputArray = new Uint8Array(rawData.length);
-
-  for (let i = 0; i < rawData.length; ++i) {
-    outputArray[i] = rawData.charCodeAt(i);
-  }
-  return outputArray;
-};
-
-const getApplicationServerKey = () => document.querySelector('[name="applicationServerKey"]').getAttribute('content');
-
-const getRegistration = () => navigator.serviceWorker.ready;
-
-const getPushSubscription = (registration) =>
-  registration.pushManager.getSubscription()
-    .then(subscription => ({ registration, subscription }));
-
-const subscribe = (registration) =>
-  registration.pushManager.subscribe({
-    userVisibleOnly: true,
-    applicationServerKey: urlBase64ToUint8Array(getApplicationServerKey()),
-  });
-
-const unsubscribe = ({ registration, subscription }) =>
-  subscription ? subscription.unsubscribe().then(() => registration) : registration;
-
-const sendSubscriptionToBackend = (subscription) => {
-  const params = { subscription };
-
-  const me = store.getState().getIn(['meta', 'me']);
-  if (me) {
-    const data = pushNotificationsSetting.get(me);
-    if (data) {
-      params.data = data;
-    }
-  }
-
-  return axios.post('/api/web/push_subscriptions', params).then(response => response.data);
-};
-
-// 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 () {
-  store.dispatch(setBrowserSupport(supportsPushNotifications));
-  const me = store.getState().getIn(['meta', 'me']);
-
-  if (me && !pushNotificationsSetting.get(me)) {
-    const alerts = store.getState().getIn(['push_notifications', 'alerts']);
-    if (alerts) {
-      pushNotificationsSetting.set(me, { alerts: alerts });
-    }
-  }
-
-  if (supportsPushNotifications) {
-    if (!getApplicationServerKey()) {
-      console.error('The VAPID public key is not set. You will not be able to receive Web Push Notifications.');
-      return;
-    }
-
-    getRegistration()
-      .then(getPushSubscription)
-      .then(({ registration, subscription }) => {
-        if (subscription !== null) {
-          // We have a subscription, check if it is still valid
-          const currentServerKey = (new Uint8Array(subscription.options.applicationServerKey)).toString();
-          const subscriptionServerKey = urlBase64ToUint8Array(getApplicationServerKey()).toString();
-          const serverEndpoint = store.getState().getIn(['push_notifications', '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;
-          } else {
-            // Something went wrong, try to subscribe again
-            return unsubscribe({ registration, subscription }).then(subscribe).then(sendSubscriptionToBackend);
-          }
-        }
-
-        // No subscription, try to subscribe
-        return subscribe(registration).then(sendSubscriptionToBackend);
-      })
-      .then(subscription => {
-        // 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)) {
-          store.dispatch(setSubscription(subscription));
-          if (me) {
-            pushNotificationsSetting.set(me, { alerts: subscription.alerts });
-          }
-        }
-      })
-      .catch(error => {
-        if (error.code === 20 && error.name === 'AbortError') {
-          console.warn('Your browser supports Web Push Notifications, but does not seem to implement the VAPID protocol.');
-        } else if (error.code === 5 && error.name === 'InvalidCharacterError') {
-          console.error('The VAPID public key seems to be invalid:', getApplicationServerKey());
-        }
-
-        // Clear alerts and hide UI settings
-        store.dispatch(clearSubscription());
-        if (me) {
-          pushNotificationsSetting.remove(me);
-        }
-
-        try {
-          getRegistration()
-            .then(getPushSubscription)
-            .then(unsubscribe);
-        } catch (e) {
-
-        }
-      });
-  } else {
-    console.warn('Your browser does not support Web Push Notifications.');
-  }
-}