diff --git a/api/src/controllers/consommation.js b/api/src/controllers/consommation.js
index e7ce65c31..495bcea2d 100644
--- a/api/src/controllers/consommation.js
+++ b/api/src/controllers/consommation.js
@@ -308,4 +308,21 @@ router.get(
})
);
+router.post(
+ "/find-missing-own-drink",
+ catchErrors(async (req, res) => {
+ const { drinkKey, matomoId } = req.body;
+ if (!matomoId) return res.status(400).json({ ok: false, error: "no matomo id" });
+
+ // find user with matomoId
+ let user = await prisma.user.findUnique({ where: { matomo_id: matomoId } });
+
+ const conso = await prisma.consommation.findFirst({
+ where: { userId: user.id, drinkKey },
+ });
+
+ return res.status(200).send({ ok: true, data: conso });
+ })
+);
+
module.exports = router;
diff --git a/expo/App.jsx b/expo/App.jsx
index 1918367c9..23ba7f8fd 100644
--- a/expo/App.jsx
+++ b/expo/App.jsx
@@ -2,7 +2,7 @@ import "react-native-get-random-values";
import React, { useEffect, useState } from "react";
import * as Sentry from "@sentry/react-native";
import { SafeAreaProvider } from "react-native-safe-area-context";
-import { RecoilRoot } from "recoil";
+import { RecoilRoot, useSetRecoilState } from "recoil";
import dayjs from "dayjs";
import * as SplashScreen from "expo-splash-screen";
import * as Application from "expo-application";
@@ -26,7 +26,10 @@ import {
migrateMissingDrinkKey,
sendPreviousDrinksToDB,
} from "./src/migrations";
-import { reconciliateDrinksToDB, reconciliateGoalToDB } from "./src/reconciliations";
+import { fixMissingDrinkKey, reconciliateDrinksToDB, reconciliateGoalToDB } from "./src/reconciliations";
+import { drinksState, ownDrinksCatalogState } from "./src/recoil/consos";
+import { drinksByWeekState, goalsState } from "./src/recoil/gains";
+import { getInitValueFromStorage } from "./src/recoil/utils";
dayjs.locale("fr");
dayjs.extend(isSameOrAfter);
@@ -34,8 +37,7 @@ dayjs.extend(weekday);
SplashScreen.preventAutoHideAsync();
-const release =
- getBundleId() + "@" + Application.nativeApplicationVersion + "+" + Application.nativeBuildVersion; // ex : com.addicto.v1@1.18.0+198
+const release = getBundleId() + "@" + Application.nativeApplicationVersion + "+" + Application.nativeBuildVersion; // ex : com.addicto.v1@1.18.0+198
Sentry.init({
dsn: __DEV__ ? "" : "https://0ef6896e639948fd9ba54b861186360d@sentry.fabrique.social.gouv.fr/80",
@@ -46,15 +48,14 @@ Sentry.init({
const App = () => {
// sync everytime we open the app
const [reconciliatedDrinksToDB, setReconciliatedDrinksToDB] = useState(false);
+ const [fixedMissingDrinkKey, setFixedMissingDrinkKey] = useState(false);
const [reconciliatedGoalsToDB, setReconciliatedGoalsToDB] = useState(false);
// migrate only once if not yet done
// TODO: clean migrations when it's time
- const [_hasSentPreviousDrinksToDB, setHasSentPreviousDrinksToDB] =
- useState(hasSentPreviousDrinksToDB);
+ const [_hasSentPreviousDrinksToDB, setHasSentPreviousDrinksToDB] = useState(hasSentPreviousDrinksToDB);
const [_hasCleanConsoAndCatalog, setHasCleanConsoAndCatalog] = useState(hasCleanConsoAndCatalog);
- const [_hasMigrateMissingDrinkKey, sethasMigrateMissingDrinkKey] =
- useState(hasMigrateMissingDrinkKey);
+ const [_hasMigrateMissingDrinkKey, sethasMigrateMissingDrinkKey] = useState(hasMigrateMissingDrinkKey);
const [_hasMigrateFromDailyGoalToWeekly, sethasMigrateFromDailyGoalToWeekly] = useState(
hasMigrateFromDailyGoalToWeekly
);
@@ -65,6 +66,10 @@ const App = () => {
await reconciliateDrinksToDB();
setReconciliatedDrinksToDB(true);
}
+ if (!fixedMissingDrinkKey) {
+ await fixMissingDrinkKey();
+ setFixedMissingDrinkKey(true);
+ }
if (!reconciliatedGoalsToDB) {
await reconciliateGoalToDB();
setReconciliatedGoalsToDB(true);
@@ -92,6 +97,7 @@ const App = () => {
if (
!reconciliatedDrinksToDB ||
+ !fixedMissingDrinkKey ||
!reconciliatedGoalsToDB ||
!_hasSentPreviousDrinksToDB ||
!_hasCleanConsoAndCatalog ||
@@ -101,8 +107,13 @@ const App = () => {
return null;
}
+ return ;
+};
+
+function RecoiledApp() {
return (
+
@@ -110,6 +121,24 @@ const App = () => {
);
-};
+}
+
+// Why this function ?
+// because we have a FUCKING HARD TIME to manage how and when recoil is initiated
+// the default value of recoil's atoms is called AT FIRST, before any migration or reconciliation
+// so we need to re-init the atoms we want to be initiated once the migrations/reconciliations are done
+function ResetRecoilStatesAfterMigrationsAndReconciliations() {
+ const resetOwnDrinks = useSetRecoilState(ownDrinksCatalogState);
+ const resetDrinks = useSetRecoilState(drinksState);
+ const resetDrinksByWeek = useSetRecoilState(drinksByWeekState);
+ const resetGoals = useSetRecoilState(goalsState);
+ useEffect(() => {
+ resetOwnDrinks(getInitValueFromStorage("@OwnDrinks", []));
+ resetDrinks(getInitValueFromStorage("@Drinks", []));
+ resetDrinksByWeek(getInitValueFromStorage("@StoredDetaileddrinksByWeekState", []));
+ resetGoals(getInitValueFromStorage("goalsState", []));
+ }, []);
+ return null;
+}
export default Sentry.wrap(App);
diff --git a/expo/src/Router.js b/expo/src/Router.js
index b0a9e8253..429be9e4c 100644
--- a/expo/src/Router.js
+++ b/expo/src/Router.js
@@ -71,6 +71,7 @@ const TabsNavigator = ({ navigation }) => {
const showBootSplash = useRecoilValue(showBootSplashState);
const [isInCraving, setIsInCraving] = useRecoilState(isInCravingKeyState);
+
return (
<>
{
+const DrinkPersonalisation = ({ updateDrinkKey, hide, quantitySelected, setQuantitySelected, setLocalDrinksState }) => {
const route = useRoute();
const timestamp = route?.params?.timestamp;
@@ -49,6 +43,7 @@ const DrinkPersonalisation = ({
const [isUpdateWanted, setIsUpdateWanted] = useState(true);
const volumeNumber = quantitySelected?.volume ?? drink?.volume.split(" ")[0];
const saveDrink = async () => {
+ console.log("SAVE DRINK");
const formatedPrice = drinkPrice.replace(",", ".");
const formatedAlcoolPercentage = drinkAlcoolPercentage.replace(",", ".");
const formatedVolume = volumeNumber.replace(",", ".");
@@ -58,8 +53,7 @@ const DrinkPersonalisation = ({
(catalogDrink) => catalogDrink.drinkKey === drinkName && catalogDrink.isDeleted === false
) ??
ownDrinksCatalog.find(
- (catalogDrink) =>
- catalogDrink.drinkKey === updateDrinkKey && catalogDrink.isDeleted === false
+ (catalogDrink) => catalogDrink.drinkKey === updateDrinkKey && catalogDrink.isDeleted === false
);
const kCal = ((formatedAlcoolPercentage * 0.8 * formatedVolume) / 10) * 7;
const doses = Math.round((formatedAlcoolPercentage * 0.8 * formatedVolume) / 10) / 10;
@@ -102,25 +96,29 @@ const DrinkPersonalisation = ({
if (!keepGoing) return;
}
newDrink.icon = quantitySelected?.icon ?? oldDrink.icon;
- setOwnDrinksCatalog((oldState) => {
- const tempState = [...oldState, newDrink];
- return tempState;
- });
- setGlobalDrinksState((oldState) => {
- return oldState.map((oldStateDrink) =>
- oldStateDrink.drinkKey === oldDrink.drinkKey
- ? { ...oldStateDrink, drinkKey: drinkName }
- : oldStateDrink
- );
- });
- setOwnDrinksCatalog((tempState) => {
- const cleanedNewState = tempState
- .filter((tempStateDrink) => tempStateDrink.drinkKey !== newDrink.drinkKey)
- .map((oldStateDrink) =>
+ if (oldDrink.drinkKey === newDrink.drinkKey) {
+ setOwnDrinksCatalog((oldState) => {
+ return oldState.map((oldStateDrink) =>
oldStateDrink.drinkKey === oldDrink.drinkKey ? newDrink : oldStateDrink
);
- return cleanedNewState;
- });
+ });
+ } else {
+ setOwnDrinksCatalog((oldState) => {
+ const tempState = [...oldState, newDrink];
+ return tempState;
+ });
+ setGlobalDrinksState((oldState) => {
+ return oldState.map((oldStateDrink) =>
+ oldStateDrink.drinkKey === oldDrink.drinkKey ? { ...oldStateDrink, drinkKey: drinkName } : oldStateDrink
+ );
+ });
+ setOwnDrinksCatalog((tempState) => {
+ const cleanedNewState = tempState
+ .filter((tempStateDrink) => tempStateDrink.drinkKey !== newDrink.drinkKey)
+ .map((oldStateDrink) => (oldStateDrink.drinkKey === oldDrink.drinkKey ? newDrink : oldStateDrink));
+ return cleanedNewState;
+ });
+ }
const matomoId = storage.getString("@UserIdv2");
@@ -214,14 +212,16 @@ const DrinkPersonalisation = ({
{!quantitySelected?.volume && !drink ? (
setShowQuantityModal(true)}>
+ onPress={() => setShowQuantityModal(true)}
+ >
Sélectionnez une quantité
) : (
setShowQuantityModal(true)}>
+ onPress={() => setShowQuantityModal(true)}
+ >
{volumeNumber}
)}
@@ -281,10 +281,9 @@ const DrinkPersonalisation = ({
onPress={() => {
setIsUpdateWanted(false);
setShowModalUpdate(true);
- }}>
-
- Supprimer ma boisson
-
+ }}
+ >
+ Supprimer ma boisson
)}
diff --git a/expo/src/migrations.js b/expo/src/migrations.js
index 2ce6b24b4..1c14b2c7c 100644
--- a/expo/src/migrations.js
+++ b/expo/src/migrations.js
@@ -143,8 +143,8 @@ function cleanCatalog(oldDrinkCatalog) {
const alcoolPercentage = Number(oldDrink.categoryKey.split("-")[2])
? Number(oldDrink.categoryKey.split("-")[2])
: oldDrink.alcoolPercentage
- ? String(oldDrink.alcoolPercentage).replace(",", ".")
- : 5;
+ ? String(oldDrink.alcoolPercentage).replace(",", ".")
+ : 5;
// 4. create kcal and doses if they don't exist
const kcal = Math.round(((Number(alcoolPercentage) * 0.8 * volume) / 10) * 7);
@@ -172,8 +172,7 @@ function cleanCatalog(oldDrinkCatalog) {
: "ownDrink";
// 9. we fix the bug of the new cocktails
- const categoryKeyEvolution2 =
- drinkKeyEvolution1 === "ownCocktail" ? "ownCocktail" : categoryKeyEvolution1;
+ const categoryKeyEvolution2 = drinkKeyEvolution1 === "ownCocktail" ? "ownCocktail" : categoryKeyEvolution1;
newOwnDrinksCatalog.push({
drinkKey: drinkKeyEvolution2,
@@ -193,9 +192,7 @@ function cleanCatalog(oldDrinkCatalog) {
return newOwnDrinksCatalog;
}
-export const hasMigrateFromDailyGoalToWeekly = storage.getBoolean(
- "hasMigrateFromDailyGoalToWeekly"
-);
+export const hasMigrateFromDailyGoalToWeekly = storage.getBoolean("hasMigrateFromDailyGoalToWeekly");
export async function migrateFromDailyGoalToWeekly() {
try {
const drinksByDrinkingDayString = storage.getString("@StoredDetailedDrinksByDrinkingDay");
@@ -216,9 +213,7 @@ export async function migrateFromDailyGoalToWeekly() {
capture(e, {
extra: {
migration: "hasMigrateFromDailyGoalToWeekly",
- "@StoredDetailedDrinksByDrinkingDay": storage.getString(
- "@StoredDetailedDrinksByDrinkingDay"
- ),
+ "@StoredDetailedDrinksByDrinkingDay": storage.getString("@StoredDetailedDrinksByDrinkingDay"),
"@DaysWithGoalNoDrink": storage.getString("@DaysWithGoalNoDrink"),
},
user: {
diff --git a/expo/src/recoil/consos.js b/expo/src/recoil/consos.js
index 08da18ece..7f7cb9e14 100644
--- a/expo/src/recoil/consos.js
+++ b/expo/src/recoil/consos.js
@@ -16,10 +16,7 @@ export const drinksState = atom({
onSet((newValue) => {
storage.set("@Drinks", JSON.stringify(newValue));
Sentry.setExtra("drinks", newValue.slice(0, 50));
- Sentry.setExtra(
- "all-drinks",
- newValue.map(({ drinkKey, id }) => `${drinkKey}_${id}`).join("__")
- );
+ Sentry.setExtra("all-drinks", newValue.map(({ drinkKey, id }) => `${drinkKey}_${id}`).join("__"));
}),
],
});
diff --git a/expo/src/reconciliations.js b/expo/src/reconciliations.js
index 3e1bdb5e8..554a2e5b9 100644
--- a/expo/src/reconciliations.js
+++ b/expo/src/reconciliations.js
@@ -1,4 +1,5 @@
import { getMaxDrinksPerWeek, getTotalDrinksByDrinkingDay } from "./helpers/gainsHelpers";
+import { alcoolQuantityCatalog } from "./scenes/AddDrink/alcoolQuantityCatalog";
import { drinksCatalog } from "./scenes/ConsoFollowUp/drinksCatalog";
import API from "./services/api";
import { capture } from "./services/sentry";
@@ -26,10 +27,7 @@ export async function reconciliateDrinksToDB() {
},
}).then((response) => {
if (response?.ok) {
- storage.set(
- "@Drinks",
- JSON.stringify(drinks.map((drink) => ({ ...drink, isSyncedWithDB: true })))
- );
+ storage.set("@Drinks", JSON.stringify(drinks.map((drink) => ({ ...drink, isSyncedWithDB: true }))));
}
});
} catch (e) {
@@ -58,10 +56,7 @@ export async function reconciliateGoalToDB() {
const daysWithGoalNoDrink = JSON.parse(storage.getString("@DaysWithGoalNoDrink") || "[]");
const drinksByWeek = JSON.parse(storage.getString("@StoredDetaileddrinksByWeekState") || "[]");
const maxDrinksPerWeek = getMaxDrinksPerWeek(drinksByWeek);
- const totalDrinksByDrinkingDay = getTotalDrinksByDrinkingDay(
- maxDrinksPerWeek,
- daysWithGoalNoDrink
- );
+ const totalDrinksByDrinkingDay = getTotalDrinksByDrinkingDay(maxDrinksPerWeek, daysWithGoalNoDrink);
await API.post({
path: "/goal/sync",
@@ -90,3 +85,46 @@ export async function reconciliateGoalToDB() {
});
}
}
+
+export async function fixMissingDrinkKey() {
+ const drinks = JSON.parse(storage.getString("@Drinks"));
+ const ownDrinksCatalog = JSON.parse(storage.getString("@OwnDrinks") || "[]");
+ const objectCatalog = {};
+ for (const ownDrink of ownDrinksCatalog) {
+ objectCatalog[ownDrink.drinkKey] = ownDrink;
+ }
+ for (const catalogDrink of drinksCatalog) {
+ objectCatalog[catalogDrink.drinkKey] = catalogDrink;
+ }
+ for (const drink of drinks) {
+ if (!objectCatalog[drink.drinkKey]) {
+ const response = await API.post({
+ path: "/consommation/find-missing-own-drink",
+ body: {
+ drinkKey: drink.drinkKey,
+ matomoId: storage.getString("@UserIdv2"),
+ },
+ });
+ if (response.ok && response.data) {
+ const missingDrink = {
+ categoryKey: "ownDrink",
+ drinkKey: drink.drinkKey,
+ displayFeed: drink.drinkKey,
+ displayDrinkModal: drink.drinkKey,
+ volume: response.data.volume,
+ price: Number(response.data.price),
+ doses: response.data.doses,
+ icon: alcoolQuantityCatalog.find((catalog) => catalog.volume === response.data.volume)?.icon,
+ // const doses = Math.round((formatedAlcoolPercentage * 0.8 * formatedVolume) / 10) / 10;
+ alcoolPercentage: (response.data.doses * 10 * 10) / (response.data.volume * 0.8),
+ kcal: response.data.kcal,
+ custom: true,
+ isDeleted: false,
+ };
+ ownDrinksCatalog.push(missingDrink);
+ objectCatalog[drink.drinkKey] = missingDrink;
+ storage.set("@OwnDrinks", JSON.stringify(ownDrinksCatalog));
+ }
+ }
+ }
+}
diff --git a/expo/src/scenes/ConsoFollowUp/ConsoFeedDisplay.js b/expo/src/scenes/ConsoFollowUp/ConsoFeedDisplay.js
index 28e9026b3..e68a37354 100644
--- a/expo/src/scenes/ConsoFollowUp/ConsoFeedDisplay.js
+++ b/expo/src/scenes/ConsoFollowUp/ConsoFeedDisplay.js
@@ -48,9 +48,7 @@ const ConsoFeedDisplay = ({
({drink.volume})
)}
- {(isFirst(position) || isAlone(position)) && (
- {new Date(timestamp).getLocaleTime("fr")}
- )}
+ {(isFirst(position) || isAlone(position)) && {new Date(timestamp).getLocaleTime("fr")}}
>
) : (
<>
@@ -59,9 +57,7 @@ const ConsoFeedDisplay = ({
{quantity} {"Boisson inconnue"}{" "}
- {(isFirst(position) || isAlone(position)) && (
- {new Date(timestamp).getLocaleTime("fr")}
- )}
+ {(isFirst(position) || isAlone(position)) && {new Date(timestamp).getLocaleTime("fr")}}
>
)}
@@ -70,13 +66,7 @@ const ConsoFeedDisplay = ({
{showButtons && (
-
+
)}
>
diff --git a/expo/src/scenes/ConsoFollowUp/drinksCatalog.js b/expo/src/scenes/ConsoFollowUp/drinksCatalog.js
index dd14a6878..fb74fee2c 100644
--- a/expo/src/scenes/ConsoFollowUp/drinksCatalog.js
+++ b/expo/src/scenes/ConsoFollowUp/drinksCatalog.js
@@ -69,7 +69,7 @@ export const mapDrinkToDose = ({ drinkKey, quantity }, catalogObject) => {
if (drinkKey === NO_CONSO) return 0;
const drink = catalogObject[drinkKey];
if (!drink) {
- capture(new Error("drink really not found"), {
+ capture(new Error("drink really really not found"), {
extra: { drinkKey, catalogObject, function: "mapDrinkToDose" },
tags: { drinkKey },
});
@@ -83,7 +83,7 @@ export const mapDrinkToKcals = ({ drinkKey, quantity }, catalogObject) => {
if (drinkKey === NO_CONSO) return 0;
const drink = catalogObject[drinkKey];
if (!drink) {
- capture(new Error("drink really not found"), {
+ capture(new Error("drink really really not found"), {
extra: { drinkKey, catalogObject, function: "mapDrinkToKcals" },
tags: { drinkKey },
});
@@ -97,7 +97,7 @@ export const mapDrinkToPrice = ({ drinkKey, quantity }, catalogObject) => {
if (drinkKey === NO_CONSO) return 0;
const drink = catalogObject[drinkKey];
if (!drink) {
- capture(new Error("drink really not found"), {
+ capture(new Error("drink really really not found"), {
extra: { drinkKey, catalogObject, function: "mapDrinkToPrice" },
tags: { drinkKey },
});
@@ -111,7 +111,7 @@ export const getDisplayName = (drinkKey, quantity, catalogObject) => {
try {
const drink = catalogObject[drinkKey];
if (!drink) {
- capture(new Error("drink really not found"), {
+ capture(new Error("drink really really not found"), {
extra: { drinkKey, catalogObject, function: "getDisplayName" },
tags: { drinkKey },
});
@@ -131,7 +131,7 @@ export const getDisplayDrinksModalName = (drinkKey, catalogObject, quantity = 1)
try {
const drink = catalogObject[drinkKey];
if (!drink) {
- capture(new Error("drink really not found"), {
+ capture(new Error("drink really really not found"), {
extra: { drinkKey, catalogObject, function: "getDisplayDrinksModalName" },
tags: { drinkKey },
});
@@ -151,7 +151,7 @@ export const getDisplayDrinksModalName = (drinkKey, catalogObject, quantity = 1)
export const getIcon = (iconName) => {
const icon = mapIconNameToIcon[iconName];
if (!icon) {
- capture(new Error("icon not found"), { extra: { iconName, function: "getIcon" } });
+ capture(new Error("icon really really not found"), { extra: { iconName, function: "getIcon" } });
return HalfBeer;
}
return icon;