diff --git a/expo/package.json b/expo/package.json
index 51ab2c3ed..0fd8071f6 100644
--- a/expo/package.json
+++ b/expo/package.json
@@ -49,6 +49,7 @@
"expo-constants": "^16.0.2",
"expo-dev-client": "^4.0.20",
"expo-font": "^12.0.7",
+ "expo-image": "~1.12.13",
"expo-linking": "^6.3.1",
"expo-notifications": "^0.28.10",
"expo-splash-screen": "^0.27.5",
@@ -62,7 +63,6 @@
"react-native-awesome-slider": "^2.5.3",
"react-native-device-info": "^11.1.0",
"react-native-draggable-flatlist": "^4.0.1",
- "react-native-fast-image": "^8.6.3",
"react-native-gesture-handler": "~2.16.1",
"react-native-get-random-values": "^1.11.0",
"react-native-in-app-review": "^4.3.3",
diff --git a/expo/src/components/Confettis.js b/expo/src/components/Confettis.js
index 8dfef1313..0da70e0af 100644
--- a/expo/src/components/Confettis.js
+++ b/expo/src/components/Confettis.js
@@ -1,7 +1,15 @@
-import React, { useEffect } from "react";
-import Animated, { useSharedValue, useAnimatedStyle } from "react-native-reanimated";
+import React from "react";
import { View, Dimensions, StyleSheet } from "react-native";
-import FastImage from "react-native-fast-image";
+import Animated, {
+ useAnimatedStyle,
+ useSharedValue,
+ withDelay,
+ withRepeat,
+ withSequence,
+ withTiming,
+ Easing,
+} from "react-native-reanimated";
+import { Image } from "expo-image";
import ConfettiImage from "../assets/images/confetti.png";
const NUM_CONFETTI = 100;
@@ -9,98 +17,103 @@ const COLORS = ["#107ed5", "#DE285E", "#39CEC1", "#09aec5", "#FCBC49"];
const CONFETTI_SIZE = 16;
const Confettis = () => {
- const { width: screenWidth, height: screenHeight } = Dimensions.get("screen");
- const confettiProps = Array.from({ length: NUM_CONFETTI }, (_, i) => ({
- initialX: Math.random() * screenWidth,
- initialY: -screenHeight - 100, // Start off-screen
- xVel: Math.random() * 400 - 200,
- yVel: Math.random() * 150 + 400,
- angleVel: (Math.random() * 3 - 1.5) * Math.PI,
- delay: Math.floor(i / 15) * 0.3,
- elasticity: Math.random() * 0.3 + 0.1,
- color: COLORS[i % COLORS.length],
- }));
+ const { width: screenWidth, height: screenHeight } = Dimensions.get("window");
return (
-
- {confettiProps.map((props, index) => (
-
+
+ {Array.from({ length: NUM_CONFETTI }).map((_, index) => (
+
))}
);
};
-const Confetti = ({ initialX, initialY, xVel, yVel, angleVel, delay, elasticity, color }) => {
- const { width: screenWidth } = Dimensions.get("screen");
- const x = useSharedValue(initialX);
- const y = useSharedValue(initialY);
- const angle = useSharedValue(0); // Assuming initial angle is 0
- useEffect(() => {
- let frameId;
+const Confetti = ({ screenWidth, screenHeight, color, delay }) => {
+ const x = useSharedValue(Math.random() * screenWidth);
+ const y = useSharedValue(-screenHeight); // Start well above the screen
+ const rotation = useSharedValue(0);
- const animate = () => {
- "worklet";
- if (delay > 0) {
- // eslint-disable-next-line react-hooks/exhaustive-deps
- delay -= 1 / 60; // Assuming 60 FPS
- } else {
- x.value += xVel / 60;
- y.value += yVel / 60;
- angle.value += angleVel / 60;
+ // Randomize horizontal movement
+ const amplitude = Math.random() * 50 + 50; // Random amplitude between 50 and 250
+ const period = Math.random() * 2000 + 1000; // Random period between 1000 and 3000 ms
- // Check and handle left and right boundaries
- if (x.value > screenWidth - CONFETTI_SIZE) {
- x.value = screenWidth - CONFETTI_SIZE; // Adjust position
- // eslint-disable-next-line react-hooks/exhaustive-deps
- xVel *= -elasticity; // Reverse velocity
- } else if (x.value < 0) {
- x.value = 0; // Adjust position
- xVel *= -elasticity; // Reverse velocity
- }
- }
+ React.useEffect(() => {
+ y.value = withDelay(
+ delay,
+ withTiming(screenHeight + CONFETTI_SIZE, {
+ duration: 4000, // 10 seconds for a slower fall
+ easing: Easing.linear,
+ })
+ );
- frameId = requestAnimationFrame(animate);
- };
-
- frameId = requestAnimationFrame(animate);
+ x.value = withDelay(
+ delay,
+ withRepeat(
+ withSequence(
+ withTiming(x.value - amplitude, {
+ duration: period,
+ easing: Easing.inOut(Easing.ease),
+ }),
+ withTiming(x.value + amplitude, {
+ duration: period,
+ easing: Easing.inOut(Easing.ease),
+ })
+ ),
+ -1,
+ true
+ )
+ );
- return () => {
- if (frameId) {
- cancelAnimationFrame(frameId);
- }
- };
+ rotation.value = withDelay(
+ delay,
+ withRepeat(
+ withTiming(2 * Math.PI, {
+ duration: 2000,
+ easing: Easing.linear,
+ }),
+ -1
+ )
+ );
}, []);
- const animatedStyles = useAnimatedStyle(() => {
+ const animatedStyle = useAnimatedStyle(() => {
return {
transform: [
{ translateX: x.value },
{ translateY: y.value },
- { rotate: `${angle.value}rad` },
+ { rotate: `${rotation.value}rad` },
],
backgroundColor: color,
};
});
return (
-
-
+
+
);
};
const styles = StyleSheet.create({
- confettiContainer: {
- position: "absolute",
- width: CONFETTI_SIZE,
- height: CONFETTI_SIZE,
- top: 0,
- left: 0,
+ container: {
+ ...StyleSheet.absoluteFillObject,
+ pointerEvents: "none",
},
confetti: {
+ position: "absolute",
width: CONFETTI_SIZE,
height: CONFETTI_SIZE,
},
+ confettiImage: {
+ width: "100%",
+ height: "100%",
+ },
});
export default Confettis;
diff --git a/expo/src/components/ModalGainDetails.js b/expo/src/components/ModalGainDetails.js
index bffe2bd86..b7dc0c975 100644
--- a/expo/src/components/ModalGainDetails.js
+++ b/expo/src/components/ModalGainDetails.js
@@ -1,16 +1,19 @@
-import React from 'react';
-import Svg, { Path } from 'react-native-svg';
-import { View, TouchableOpacity, Text } from 'react-native';
-import Modal from './Modal';
-import { hitSlop } from '../styles/theme';
-import Confetti from './Confettis';
+import React from "react";
+import Svg, { Path } from "react-native-svg";
+import { View, TouchableOpacity, Text } from "react-native";
+import Modal from "./Modal";
+import { hitSlop } from "../styles/theme";
+import Confettis from "./Confettis";
const ModalGainDetails = ({ content, onClose }) => {
- const firstDayMonth = content?.firstDay?.split(' ')[1];
- const lastDayMonth = content?.lastDay?.split(' ')[1];
- const firstDayDisplay = firstDayMonth === lastDayMonth ? content?.firstDay?.split(' ')[0] : content?.firstDay;
- const caloriesTitle = content?.weekKcal <= content?.estimationKcal ? 'KCalories évitées' : 'KCalories en plus';
- const eurosTitle = content?.weekExpenses <= content?.estimationExpenses ? 'Euros épargnés' : 'Euros non-épargnés';
+ const firstDayMonth = content?.firstDay?.split(" ")[1];
+ const lastDayMonth = content?.lastDay?.split(" ")[1];
+ const firstDayDisplay =
+ firstDayMonth === lastDayMonth ? content?.firstDay?.split(" ")[0] : content?.firstDay;
+ const caloriesTitle =
+ content?.weekKcal <= content?.estimationKcal ? "KCalories évitées" : "KCalories en plus";
+ const eurosTitle =
+ content?.weekExpenses <= content?.estimationExpenses ? "Euros épargnés" : "Euros non-épargnés";
return (
@@ -60,7 +63,9 @@ const ModalGainDetails = ({ content, onClose }) => {
{eurosTitle}
- {content.savedExpenses}€
+
+ {content.savedExpenses}€
+
@@ -90,7 +95,9 @@ const ModalGainDetails = ({ content, onClose }) => {
{caloriesTitle}
- {content?.savedKcal}
+
+ {content?.savedKcal}
+
KCAL
@@ -105,9 +112,9 @@ const ModalGainDetails = ({ content, onClose }) => {
- {eurosTitle === 'Euros épargnés' && caloriesTitle === 'KCalories évitées' && content?.isWeekCompleted && (
-
- )}
+ {eurosTitle === "Euros épargnés" &&
+ caloriesTitle === "KCalories évitées" &&
+ content?.isWeekCompleted && }
);
};
diff --git a/expo/src/components/ModalGoal.js b/expo/src/components/ModalGoal.js
index 02bf2fd9b..0a81f38b4 100644
--- a/expo/src/components/ModalGoal.js
+++ b/expo/src/components/ModalGoal.js
@@ -1,19 +1,20 @@
-import React from 'react';
-import Svg, { Path } from 'react-native-svg';
-import { View, TouchableOpacity, Text } from 'react-native';
-import Modal from './Modal';
-import { hitSlop } from '../styles/theme';
-import CrossDefisFailed from './illustrations/icons/CrossDefisFailed';
-import CheckDefisValidated from './illustrations/icons/CheckDefisValidated';
-import TextStyled from './TextStyled';
-import InterogationMark from './illustrations/icons/InterogationMark';
-import Confetti from './Confettis';
-import OnGoingGoal from './illustrations/icons/OnGoingGoal';
+import React from "react";
+import Svg, { Path } from "react-native-svg";
+import { View, TouchableOpacity, Text } from "react-native";
+import Modal from "./Modal";
+import { hitSlop } from "../styles/theme";
+import CrossDefisFailed from "./illustrations/icons/CrossDefisFailed";
+import CheckDefisValidated from "./illustrations/icons/CheckDefisValidated";
+import TextStyled from "./TextStyled";
+import InterogationMark from "./illustrations/icons/InterogationMark";
+import Confettis from "./Confettis";
+import OnGoingGoal from "./illustrations/icons/OnGoingGoal";
const ModalGoal = ({ content, onClose }) => {
- const firstDayMonth = content?.firstDay?.split(' ')[1];
- const lastDayMonth = content?.lastDay?.split(' ')[1];
- const firstDayDisplay = firstDayMonth === lastDayMonth ? content?.firstDay?.split(' ')[0] : content?.firstDay;
+ const firstDayMonth = content?.firstDay?.split(" ")[1];
+ const lastDayMonth = content?.lastDay?.split(" ")[1];
+ const firstDayDisplay =
+ firstDayMonth === lastDayMonth ? content?.firstDay?.split(" ")[0] : content?.firstDay;
return (
@@ -32,10 +33,18 @@ const ModalGoal = ({ content, onClose }) => {
- {content?.consosWeekGoal >= 0 && content?.status === 'NoGoal' && }
- {content?.consosWeekGoal >= 0 && content?.status === 'InProgress' && }
- {content?.consosWeekGoal >= 0 && content?.status === 'Failed' && }
- {content?.consosWeekGoal >= 0 && content?.status === 'Success' && }
+ {content?.consosWeekGoal >= 0 && content?.status === "NoGoal" && (
+
+ )}
+ {content?.consosWeekGoal >= 0 && content?.status === "InProgress" && (
+
+ )}
+ {content?.consosWeekGoal >= 0 && content?.status === "Failed" && (
+
+ )}
+ {content?.consosWeekGoal >= 0 && content?.status === "Success" && (
+
+ )}
@@ -47,23 +56,29 @@ const ModalGoal = ({ content, onClose }) => {
- Consos semaine
-
- {Math.round(content?.consosWeek)}
+
+ Consos semaine
+
+
+
+ {Math.round(content?.consosWeek)}
+
- {' '}
- {Math.round(content?.consosWeek) > 1 ? 'unités' : 'unité'}
+ {" "}
+ {Math.round(content?.consosWeek) > 1 ? "unités" : "unité"}
Objectif max
{content?.consosWeekGoal >= 0 ? (
-
- {Math.round(content?.consosWeekGoal)}
+
+
+ {Math.round(content?.consosWeekGoal)}
+
- {' '}
- {Math.round(content?.consosWeekGoal) > 1 ? 'unités' : 'unité'}
+ {" "}
+ {Math.round(content?.consosWeekGoal) > 1 ? "unités" : "unité"}
) : (
@@ -73,7 +88,7 @@ const ModalGoal = ({ content, onClose }) => {
{content?.consommationContent && (
- {content?.consommationContent?.split('__')?.map((string, index) => {
+ {content?.consommationContent?.split("__")?.map((string, index) => {
return (
{string}
@@ -91,28 +106,36 @@ const ModalGoal = ({ content, onClose }) => {
- Jours où j'ai bu
-
- {content?.drinkingDays}
+
+ Jours où j'ai bu
+
+
+
+ {content?.drinkingDays}
+
- {' '}
- {content?.drinkingDays > 1 ? 'jours' : 'jour'}
+ {" "}
+ {content?.drinkingDays > 1 ? "jours" : "jour"}
- Objectif max
-
- {content?.drinkingDaysGoal}
+
+ Objectif max
+
+
+
+ {content?.drinkingDaysGoal}
+
- {' '}
- {content?.drinkingDaysGoal > 1 ? 'jours' : 'jour'}
+ {" "}
+ {content?.drinkingDaysGoal > 1 ? "jours" : "jour"}
- {content?.drinkingDaysContent?.split('__')?.map((string, index) => {
+ {content?.drinkingDaysContent?.split("__")?.map((string, index) => {
return (
{string}
@@ -124,7 +147,7 @@ const ModalGoal = ({ content, onClose }) => {
)}
- {content?.status === 'Success' && }
+ {content?.status === "Success" && }
);
};
diff --git a/expo/src/components/SwitchButtons.js b/expo/src/components/SwitchButtons.js
index 5dfc2e3d2..6761c067e 100644
--- a/expo/src/components/SwitchButtons.js
+++ b/expo/src/components/SwitchButtons.js
@@ -23,7 +23,8 @@ const SwitchButtons = ({ leftContent, rightContent, handleSwitchChange, initPosi
}),
]).start();
},
- [initPosition, translateX]
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ []
);
const onTerminate = async (evt) => {
const newX = evt.nativeEvent.locationX;
diff --git a/expo/src/scenes/Badges/BadgeModal.js b/expo/src/scenes/Badges/BadgeModal.js
index 7d06b1cbc..ea76e1acb 100644
--- a/expo/src/scenes/Badges/BadgeModal.js
+++ b/expo/src/scenes/Badges/BadgeModal.js
@@ -12,7 +12,7 @@ import InAppReview from "react-native-in-app-review";
import Svg, { Path } from "react-native-svg";
import { useSetRecoilState } from "recoil";
import ButtonPrimary from "../../components/ButtonPrimary";
-import Confetti from "../../components/Confettis";
+import Confettis from "../../components/Confettis";
import H1 from "../../components/H1";
import TextStyled from "../../components/TextStyled";
import { badgesCatalogState, badgesState } from "../../recoil/badges";
@@ -162,7 +162,7 @@ const BadgeModal = ({ navigation, route }) => {
)}
- {badge?.showConfettis && }
+ {badge?.showConfettis && }
);
};
diff --git a/expo/yarn.lock b/expo/yarn.lock
index 2ef926da1..10ad9cc1c 100644
--- a/expo/yarn.lock
+++ b/expo/yarn.lock
@@ -6119,6 +6119,15 @@ __metadata:
languageName: node
linkType: hard
+"expo-image@npm:~1.12.13":
+ version: 1.12.13
+ resolution: "expo-image@npm:1.12.13"
+ peerDependencies:
+ expo: "*"
+ checksum: 8ca3844419ace3c37e75db89a7a1b955b3d82ebd1a4987ca7f80d25f89ed910bb5ba3d96756bcf7fe6b90ca070c14fc6a49b5bf0f5a8cea40dc2b4d135f59960
+ languageName: node
+ linkType: hard
+
"expo-json-utils@npm:~0.13.0":
version: 0.13.1
resolution: "expo-json-utils@npm:0.13.1"
@@ -10090,6 +10099,7 @@ __metadata:
expo-constants: "npm:^16.0.2"
expo-dev-client: "npm:^4.0.20"
expo-font: "npm:^12.0.7"
+ expo-image: "npm:~1.12.13"
expo-linking: "npm:^6.3.1"
expo-notifications: "npm:^0.28.10"
expo-splash-screen: "npm:^0.27.5"
@@ -10106,7 +10116,6 @@ __metadata:
react-native-awesome-slider: "npm:^2.5.3"
react-native-device-info: "npm:^11.1.0"
react-native-draggable-flatlist: "npm:^4.0.1"
- react-native-fast-image: "npm:^8.6.3"
react-native-gesture-handler: "npm:~2.16.1"
react-native-get-random-values: "npm:^1.11.0"
react-native-in-app-review: "npm:^4.3.3"
@@ -10871,16 +10880,6 @@ __metadata:
languageName: node
linkType: hard
-"react-native-fast-image@npm:^8.6.3":
- version: 8.6.3
- resolution: "react-native-fast-image@npm:8.6.3"
- peerDependencies:
- react: ^17 || ^18
- react-native: ">=0.60.0"
- checksum: 4ebeae53ffeadf1e7e5e133c1cfda81f9efe978f9ece10c732a0338149fe9104681bfdabc8427dad7c934b02bfcbd0e205bb6ca43e2844abc6054630ebe34454
- languageName: node
- linkType: hard
-
"react-native-gesture-handler@npm:~2.16.1":
version: 2.16.2
resolution: "react-native-gesture-handler@npm:2.16.2"