diff --git a/packages/mobile/src/components/enable-push-notifications-drawer/EnablePushNotificationsDrawer.tsx b/packages/mobile/src/components/enable-push-notifications-drawer/EnablePushNotificationsDrawer.tsx index bd72108107..9499c74ff4 100644 --- a/packages/mobile/src/components/enable-push-notifications-drawer/EnablePushNotificationsDrawer.tsx +++ b/packages/mobile/src/components/enable-push-notifications-drawer/EnablePushNotificationsDrawer.tsx @@ -64,19 +64,18 @@ const actions = [ } ] -const useStyles = makeStyles(({ palette }) => ({ +const useStyles = makeStyles(({ palette, spacing }) => ({ drawer: { - display: 'flex', flexDirection: 'column', justifyContent: 'space-evenly', alignItems: 'center', - padding: 16, - paddingTop: 64, - paddingBottom: 32 + paddingHorizontal: spacing(4), + paddingTop: spacing(12), + paddingBottom: spacing(8) }, cta: { - marginTop: 16, + marginTop: spacing(4), fontSize: 28 }, @@ -88,24 +87,21 @@ const useStyles = makeStyles(({ palette }) => ({ }, top: { - marginBottom: 32, - display: 'flex', + marginBottom: spacing(8), flexDirection: 'column', alignItems: 'center' }, actions: { - display: 'flex', flexDirection: 'column', alignItems: 'flex-start', - marginBottom: 32 + marginBottom: spacing(8) }, action: { - display: 'flex', flexDirection: 'row', alignItems: 'center', - marginBottom: 12 + marginBottom: spacing(3) }, actionText: { @@ -114,7 +110,7 @@ const useStyles = makeStyles(({ palette }) => ({ }, actionIcon: { - marginRight: 16 + marginRight: spacing(4) } })) diff --git a/packages/mobile/src/components/notification-reminder/NotificationReminder.tsx b/packages/mobile/src/components/notification-reminder/NotificationReminder.tsx index 3f80d25509..95a7a48ad1 100644 --- a/packages/mobile/src/components/notification-reminder/NotificationReminder.tsx +++ b/packages/mobile/src/components/notification-reminder/NotificationReminder.tsx @@ -3,7 +3,6 @@ import { useCallback } from 'react' import { getHasCompletedAccount } from 'common/store/pages/signon/selectors' import { checkNotifications, RESULTS } from 'react-native-permissions' import { useDispatch, useSelector } from 'react-redux' -import type { Dispatch } from 'redux' import useSessionCount from 'app/hooks/useSessionCount' import { setVisibility } from 'app/store/drawers/slice' @@ -23,21 +22,9 @@ const NotificationReminderInternal = () => { const dispatch = useDispatch() // Sets up reminders to turn on push notifications - const reminder = useCallback(() => { - remindUserToTurnOnNotifications(dispatch) - }, [dispatch]) - - useSessionCount(reminder, REMINDER_FREQUENCY, FIRST_REMINDER_SESSION) - - // No UI component - return null -} - -// Sends a notification to the WebApp to turn on push notifications if we're in the DENIED -// state. Is called from the `NotificationsReminder` component as well as `handleMessage` -export const remindUserToTurnOnNotifications = (dispatch: Dispatch) => { - checkNotifications() - .then(({ status }) => { + const remindUserToTurnOnNotifications = useCallback(async () => { + try { + const { status } = await checkNotifications() switch (status) { case RESULTS.UNAVAILABLE: // Notifications are not available (on this device / in this context). @@ -63,9 +50,18 @@ export const remindUserToTurnOnNotifications = (dispatch: Dispatch) => { setVisibility({ drawer: 'EnablePushNotifications', visible: true }) ) } - }) - .catch((error) => { + } catch (error) { // Not sure what happened, but swallow the error. Not worth blocking on. console.error(error) - }) + } + }, [dispatch]) + + useSessionCount( + remindUserToTurnOnNotifications, + REMINDER_FREQUENCY, + FIRST_REMINDER_SESSION + ) + + // No UI component + return null } diff --git a/packages/mobile/src/screens/settings-screen/NotificationSettingsScreen.tsx b/packages/mobile/src/screens/settings-screen/NotificationSettingsScreen.tsx index 106b730433..f96fc58be1 100644 --- a/packages/mobile/src/screens/settings-screen/NotificationSettingsScreen.tsx +++ b/packages/mobile/src/screens/settings-screen/NotificationSettingsScreen.tsx @@ -7,7 +7,6 @@ import { useDispatch } from 'react-redux' import { useEffectOnce } from 'react-use' import { Screen, ScreenContent } from 'app/components/core' -import { remindUserToTurnOnNotifications } from 'app/components/notification-reminder/NotificationReminder' import { useFeatureFlag } from 'app/hooks/useRemoteConfig' import { Divider } from './Divider' @@ -35,7 +34,6 @@ export const NotificationSettingsScreen = () => { useEffectOnce(() => { dispatch(getPushNotificationSettings()) dispatch(getNotificationSettings()) - remindUserToTurnOnNotifications(dispatch) }) return ( diff --git a/packages/mobile/src/screens/signon/SignOn.tsx b/packages/mobile/src/screens/signon/SignOn.tsx index 44bdadee81..f0891e7db7 100644 --- a/packages/mobile/src/screens/signon/SignOn.tsx +++ b/packages/mobile/src/screens/signon/SignOn.tsx @@ -36,7 +36,6 @@ import ValidationIconX from 'app/assets/images/iconValidationX.svg' import signupCTA from 'app/assets/images/signUpCTA.png' import Button from 'app/components/button' import LoadingSpinner from 'app/components/loading-spinner' -import { remindUserToTurnOnNotifications } from 'app/components/notification-reminder/NotificationReminder' import useAppState from 'app/hooks/useAppState' import { useToast } from 'app/hooks/useToast' import { screen, track, make } from 'app/services/analytics' @@ -386,8 +385,6 @@ const SignOn = ({ navigation }: SignOnProps) => { setPassword('') }, 1000) - remindUserToTurnOnNotifications(dispatch) - return () => { clearTimeout(timeout) } diff --git a/packages/mobile/src/store/settings/sagas.ts b/packages/mobile/src/store/settings/sagas.ts index 6a20ef88b4..c239c3cb29 100644 --- a/packages/mobile/src/store/settings/sagas.ts +++ b/packages/mobile/src/store/settings/sagas.ts @@ -7,17 +7,33 @@ import { PushNotificationSetting, settingsPageActions as actions, getContext, - waitForValue + waitForValue, + waitForAccount, + settingsPageActions } from '@audius/common' import { waitForRead } from 'audius-client/src/utils/sagaHelpers' import commonSettingsSagas from 'common/store/pages/settings/sagas' import { mapValues } from 'lodash' -import { select, call, put, takeEvery } from 'typed-redux-saga' +import { RESULTS, checkNotifications } from 'react-native-permissions' +import { select, call, put, takeEvery, take } from 'typed-redux-saga' import PushNotifications from 'app/notifications' -const { getPushNotificationSettings } = settingsPageSelectors -const { getAccountUser } = accountSelectors +import { setVisibility } from '../drawers/slice' + +const { getPushNotificationSettings, SET_PUSH_NOTIFICATION_SETTINGS } = + settingsPageActions +const { getPushNotificationSettings: selectPushNotificationSettings } = + settingsPageSelectors +const { getAccountUser, getHasAccount } = accountSelectors + +function* getIsMobilePushEnabled() { + yield* put(getPushNotificationSettings()) + yield* take(SET_PUSH_NOTIFICATION_SETTINGS) + const { [PushNotificationSetting.MobilePush]: isMobilePushEnabled } = + yield* select(selectPushNotificationSettings) + return isMobilePushEnabled +} export function* deregisterPushNotifications() { const audiusBackendInstance = yield* getContext('audiusBackendInstance') @@ -26,21 +42,36 @@ export function* deregisterPushNotifications() { yield* call(audiusBackendInstance.deregisterDeviceToken, token) } -/* - * Runs once at startup and re-registers the device-token in case it changes - */ -function* reregisterDeviceToken() { +function* registerDeviceToken() { + let { token, os } = yield* call([PushNotifications, 'getToken']) + + if (!token) { + yield* call([PushNotifications, 'requestPermission']) + ;({ token, os } = yield* call([PushNotifications, 'getToken'])) + } + const audiusBackend = yield* getContext('audiusBackendInstance') - const hasPermission = yield* call([PushNotifications, 'hasPermission']) - if (hasPermission) { - const { token, os } = yield* call([PushNotifications, 'getToken']) - yield* call(audiusBackend.registerDeviceToken, token, os) + yield* call(audiusBackend.registerDeviceToken, token, os) +} + +function* reregisterDeviceTokenOnStartup() { + yield* call(waitForAccount) + const isSignedIn = yield* select(getHasAccount) + if (!isSignedIn) return + + const { status } = yield* call(checkNotifications) + const isMobilePushEnabled = yield* call(getIsMobilePushEnabled) + + if ( + (status === RESULTS.GRANTED || status === RESULTS.LIMITED) && + isMobilePushEnabled + ) { + yield* call(registerDeviceToken) } } function* enablePushNotifications() { - yield* call([PushNotifications, 'requestPermission']) - const { token, os } = yield* call([PushNotifications, 'getToken']) + yield* call(registerDeviceToken) const audiusBackendInstance = yield* getContext('audiusBackendInstance') @@ -52,8 +83,6 @@ function* enablePushNotifications() { // have one right away when this function is called) yield* call(waitForValue, getAccountUser) yield* call(audiusBackendInstance.updatePushNotificationSettings, newSettings) - - yield* call(audiusBackendInstance.registerDeviceToken, token, os) } function* disablePushNotifications() { @@ -128,7 +157,7 @@ function* watchUpdatePushNotificationSettings() { } else { if (isOn === undefined) { const pushNotificationSettings = yield* select( - getPushNotificationSettings + selectPushNotificationSettings ) isOn = !pushNotificationSettings[action.notificationType] } @@ -149,27 +178,29 @@ function* watchUpdatePushNotificationSettings() { } function* watchRequestPushNotificationPermissions() { - yield* takeEvery( - actions.REQUEST_PUSH_NOTIFICATION_PERMISSIONS, - function* (_action: actions.RequestPushNotificationPermissions) { - const hasPermissions = yield* call([PushNotifications, 'hasPermission']) - if (!hasPermissions) { - // Request permission to send push notifications and enable all if accepted - yield* put( - actions.togglePushNotificationSetting( - PushNotificationSetting.MobilePush, - true - ) - ) - } + yield* takeEvery(actions.REQUEST_PUSH_NOTIFICATION_PERMISSIONS, function* () { + const { status } = yield* call(checkNotifications) + const isMobilePushEnabled = yield* call(getIsMobilePushEnabled) + + if ( + (status === RESULTS.GRANTED || status === RESULTS.LIMITED) && + isMobilePushEnabled + ) { + yield* call(registerDeviceToken) + } else if (status === RESULTS.BLOCKED || status === RESULTS.UNAVAILABLE) { + // do nothing + } else { + yield* put( + setVisibility({ drawer: 'EnablePushNotifications', visible: true }) + ) } - ) + }) } export default function sagas() { return [ ...commonSettingsSagas(), - reregisterDeviceToken, + reregisterDeviceTokenOnStartup, watchGetPushNotificationSettings, watchUpdatePushNotificationSettings, watchRequestPushNotificationPermissions diff --git a/packages/mobile/src/store/sign-up/sagas/watchSignUpSucceededSaga.ts b/packages/mobile/src/store/sign-up/sagas/watchSignUpSucceededSaga.ts index 83be4914aa..3d4629f4a6 100644 --- a/packages/mobile/src/store/sign-up/sagas/watchSignUpSucceededSaga.ts +++ b/packages/mobile/src/store/sign-up/sagas/watchSignUpSucceededSaga.ts @@ -6,7 +6,6 @@ import { } from 'common/store/pages/signon/selectors' import { takeEvery, put, select } from 'typed-redux-saga' -import { remindUserToTurnOnNotifications } from 'app/components/notification-reminder/NotificationReminder' import { EventNames } from 'app/types/analytics' export function* watchSignUpSucceeded() { @@ -33,6 +32,4 @@ function* handleSignUpSucceeded() { handle: handleField.value }) ) - - remindUserToTurnOnNotifications(put) } diff --git a/packages/web/src/common/store/pages/signon/sagas.js b/packages/web/src/common/store/pages/signon/sagas.js index 73598f6f68..9158c3ea93 100644 --- a/packages/web/src/common/store/pages/signon/sagas.js +++ b/packages/web/src/common/store/pages/signon/sagas.js @@ -5,7 +5,6 @@ import { Genre, MAX_HANDLE_LENGTH, Name, - PushNotificationSetting, accountActions, accountSelectors, cacheUsersSelectors, @@ -53,8 +52,7 @@ import { getRouteOnCompletion, getSignOn } from './selectors' import { FollowArtistsCategory, Pages } from './types' import { checkHandle } from './verifiedChecker' -const { requestPushNotificationPermissions, togglePushNotificationSetting } = - settingsPageActions +const { requestPushNotificationPermissions } = settingsPageActions const { getFeePayer } = solanaSelectors const { saveCollection } = collectionsSocialActions const { getUsers } = cacheUsersSelectors @@ -439,13 +437,7 @@ function* signUp() { const isNativeMobile = yield getContext('isNativeMobile') if (isNativeMobile) { - // Request permission to send push notifications and enable all if accepted - yield put( - togglePushNotificationSetting( - PushNotificationSetting.MobilePush, - true - ) - ) + yield put(requestPushNotificationPermissions()) } else { // Set the has request browser permission to true as the signon provider will open it setHasRequestedBrowserPermission() @@ -545,8 +537,6 @@ function* signIn(action) { yield put(signOnActions.resetSignOn()) const isNativeMobile = yield getContext('isNativeMobile') if (isNativeMobile) { - // If permissions not already enabled, request permission to send push notifications - // and enable all if accepted yield put(requestPushNotificationPermissions()) } else { setHasRequestedBrowserPermission()