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"