Skip to content

Commit

Permalink
fix: Delete user when no more notifications (#914)
Browse files Browse the repository at this point in the history
* update common

* Fix optimistic UI
  • Loading branch information
amaury1093 authored Mar 6, 2021
1 parent 01c8855 commit d1d34a3
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 55 deletions.
90 changes: 47 additions & 43 deletions App/Screens/Home/Footer/SelectNotifications/SelectNotifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,22 @@ import { t } from '../../../../localization';
import { ApiContext } from '../../../../stores';
import { AmplitudeEvent, track } from '../../../../util/amplitude';
import { promiseToTE } from '../../../../util/fp';
import { createUser, getUser, updateUser } from '../../../../util/axios';
import {
createUser,
deleteUser,
getUser,
updateUser,
} from '../../../../util/axios';
import { sentryError } from '../../../../util/sentry';
import * as theme from '../../../../util/theme';

const notificationsValues = ['never', 'daily', 'weekly', 'monthly'] as const;

/**
* Key in the AsyncStorage for the mongo userId.
* Key in the AsyncStorage for the last chosen frequency. It's used for
* optimistic UI in the frequency selector.
*/
const KEY_MONGO_USER = 'mongoUser';
const KEY_LAST_CHOSEN_FREQUENCY = 'lastChosenFrequency';

/**
* Capitalize a string.
Expand Down Expand Up @@ -111,55 +117,53 @@ export function SelectNotifications(

// Fetch data from backend.
useEffect(() => {
AsyncStorage.getItem(KEY_MONGO_USER)
.then((serializedUser) => {
if (serializedUser) {
const user = JSON.parse(serializedUser) as MongoUser;
console.log(
`<SelectNotifications> - Got user ${JSON.stringify(
user
)} from AsyncStorage.`
);

setCurrentUser(user);

if (!user.expoReport?.expoPushToken) {
AsyncStorage.getItem(KEY_LAST_CHOSEN_FREQUENCY)
.then((lastChosenFrequency) => {
if (lastChosenFrequency) {
if (
!(notificationsValues as readonly string[]).includes(
lastChosenFrequency
)
) {
throw new Error(
'<SelectNotifications> - User in AsyncStorage does not have expoPushToken.'
`<SelectNotifications> - Got unknown frequency "${lastChosenFrequency}" from AsyncStorage.`
);
}

// Retrieve from backend the latest version of current user.
return getUser(user.expoReport.expoPushToken);
} else {
const f = lastChosenFrequency as Frequency;
console.log(
'<SelectNotifications> - No user found in AsyncStorage.'
`<SelectNotifications> - Got frequency "${f}" from AsyncStorage.`
);

// If the user have gave the "Notifications" permission,
// then we fetch the user from the backend.
return Permissions.getAsync(
Permissions.NOTIFICATIONS
).then(({ status }) =>
status === 'granted'
? Notifications.getExpoPushTokenAsync().then(
({ data }) => getUser(data)
)
: undefined
);
// We're optimistic that the backend frequency is the same
// as the one saved in AsyncStorage, so we set it.
setOptimisticNotif(f);
}
})
.then(() => {
return Permissions.getAsync(
Permissions.NOTIFICATIONS
).then(({ status }) =>
status === 'granted'
? Notifications.getExpoPushTokenAsync().then(
({ data }) => getUser(data)
)
: undefined
);
})
.then((user) => user && setCurrentUser(user))
.catch(sentryError('SelectNotifications'));
}, []);

// Update currentUser in AsyncStorage each time it changes.
// Update lastChosenFrequency in AsyncStorage each time it changes.
useEffect(() => {
currentUser &&
AsyncStorage.setItem(
KEY_MONGO_USER,
JSON.stringify(currentUser)
).catch(sentryError('SelectNotifications'));
(currentUser?.expoReport?.frequency
? AsyncStorage.setItem(
KEY_LAST_CHOSEN_FREQUENCY,
currentUser.expoReport.frequency
)
: AsyncStorage.removeItem(KEY_LAST_CHOSEN_FREQUENCY)
).catch(sentryError('SelectNotifications'));
}, [currentUser]);

// This state is used for optimistic UI: right after the user clicks, we set
Expand Down Expand Up @@ -254,9 +258,9 @@ export function SelectNotifications(
}
} else {
if (notifications.frequency === 'never') {
return updateUser(currentUser._id, {
expoReport: (null as unknown) as undefined,
});
return deleteUser(currentUser._id).then(
() => undefined // Set currentUser to undefined after deletion.
);
} else {
return updateUser(currentUser._id, {
expoReport: {
Expand All @@ -270,15 +274,15 @@ export function SelectNotifications(
),
TE.chain(
sideEffect((user) => {
user && setCurrentUser(user);
setCurrentUser(user);

return TE.right(undefined);
})
),
TE.fold(
(error) => {
sentryError('SelectNotifications')(error);
setOptimisticNotif('never');
setOptimisticNotif(undefined);

track('HOME_SCREEN_NOTIFICATIONS_ERROR');

Expand Down
16 changes: 16 additions & 0 deletions App/util/axios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,22 @@ export function updateUser(
});
}

export function deleteUser(userId: string): Promise<MongoUser> {
console.log(`<Axios> - DELETE /api/users/${userId}`);

return axios
.delete<MongoUser>(
`${
Constants.manifest.extra.backendUrl as string
}/api/users/${userId}`,
axiosConfig
)
.then(({ data }) => data)
.catch((err) => {
throw axiosErrorToError(err);
});
}

function axiosErrorToError(err: AxiosError) {
return new Error(`${err.message}: ${JSON.stringify(err.response?.data)}`);
}
4 changes: 2 additions & 2 deletions App/util/sentry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ const UNTRACKED_ERRORS = [
// No results from data providers
'does not have PM2.5 measurings right now',
'Cannot normalize, got 0 result',
'Request to fetch API data timed out.',
'Request to fetch API data timed out',
// First time fetching a user
'No user with "ExponentPushToken',
'No user with',
];

/**
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"@react-native-community/masked-view": "0.1.10",
"@react-navigation/native": "^5.9.3",
"@react-navigation/stack": "^5.14.3",
"@shootismoke/ui": "^0.8.7",
"@shootismoke/ui": "^0.8.8",
"@types/i18n-js": "^3.8.0",
"@types/react-native": "~0.63.2",
"date-fns": "^2.17.0",
Expand Down
18 changes: 9 additions & 9 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2492,10 +2492,10 @@
"@types/debug" "^4.1.5"
debug "^4.1.1"

"@shootismoke/dataproviders@^0.8.7":
version "0.8.7"
resolved "https://registry.yarnpkg.com/@shootismoke/dataproviders/-/dataproviders-0.8.7.tgz#f17ee14b640ef9fbb5bd950916a85fe56401fdf7"
integrity sha512-6leoUJNyoB8GCFq7D/3SWH/tKEu5XHct0u/+IlwhMKfI3bVg1ZjnbMJF7WHLd5a7okGP5yiBIUAAbz3I2AaU3Q==
"@shootismoke/dataproviders@^0.8.8":
version "0.8.8"
resolved "https://registry.yarnpkg.com/@shootismoke/dataproviders/-/dataproviders-0.8.8.tgz#fdfae6d0a74be8778703b8a23dcb6f7d91dbbc98"
integrity sha512-yxVmcWFAvXbCvS2e5ZExv05/1+pjIW/W1zS0eUwks4EdpT1TkOeZcA5TLs3WpB+QYIKCYJV2Ca4j82fQmvDihA==
dependencies:
"@shootismoke/convert" "^0.8.7"
axios "^0.21.0"
Expand All @@ -2504,12 +2504,12 @@
fp-ts "^2.1.1"
io-ts "^2.0.1"

"@shootismoke/ui@^0.8.7":
version "0.8.7"
resolved "https://registry.yarnpkg.com/@shootismoke/ui/-/ui-0.8.7.tgz#0fbf47a132f159b2fa14a7f78abb7891df1069e5"
integrity sha512-cPejm5ZPQC25171bXXftUqEnhkAPmMOAtexXx8cFnM3gVlbckq6TEHsjeQZXDWyko+L1NaemUt5g7SPYQmEugQ==
"@shootismoke/ui@^0.8.8":
version "0.8.8"
resolved "https://registry.yarnpkg.com/@shootismoke/ui/-/ui-0.8.8.tgz#7a2f0a56a798a128380554b4452de504cade793a"
integrity sha512-Ep/Uh2t2s7dmkpXE2Lf/4GHwP3gGMmLQ2atlR9X/CBfD30m+BTz8vqcvbgUN89nRoMZYHCHHalj28IEjJ17sOQ==
dependencies:
"@shootismoke/dataproviders" "^0.8.7"
"@shootismoke/dataproviders" "^0.8.8"
"@sindresorhus/slugify" "^1.1.0"
"@types/async-retry" "^1.4.2"
"@types/haversine" "^1.1.4"
Expand Down

0 comments on commit d1d34a3

Please sign in to comment.